一、碎碎念念
(一)React 定义
官方定义:用于构建 Web 和原生交互界面的库。其核心概念组件的思想和Vue存在很大的相似之处
(二)来龙去脉
我们知道对于前端来说,主要的任务就是构建用于界面,而构建用于界面离不开三个技术:
- HTML:构建页面的结构
- CSS:构建页面的样式
- JavaScript:页面动态内容和交互
使用最原生的HTML、CSS、JavaScript可以构建完整的用户界面,但是会存在很多问题
- 比如操作DOM兼容性的问题;
- 比如过多兼容性代码的冗余问题;
- 比如代码组织和规范的问题;
所以,一直以来前端开发人员都在需求可以让自己开发更方便的JavaScript库,在过去的很长时间内,jQuery是被使用最多的JavaScript库;在过去的一份调查中显示,全球前10,000个访问最高的网站中,有65%使用了jQuery,是当时最受欢迎的JavaScript库;但是越来越多的公司开始慢慢不再使用jQuery,包括程序员使用最多的GitHub;
如今前端最为流行的三大框架是:Vue、React、Augular
而Angular在国内并不是特别受欢迎,尤其是Angular目前的版本对TypeScript还有要求的情况下,Vue和React是国内最为流行的两个框架,而他们都是帮助我们来构建用户界面的JavaScript库。
(三)React起源
React是2013年,Facebook开源的JavaScript框架,那么当时为什么Facebook要推出这样一款框架呢?
这个源于一个需求,所产生的bug:
该功能上线之后,总是出现bug;三个消息的数字在发生变化时,过多的操作很容易产生bug。bug是否可以修复呢?当然可以修复,但是Facebook的工程师并不满足于此。他们开始思考为什么会产生这样的问题,在传统的开发模式中,我们过多的去操作界面的细节(前端、iOS、Android),并且需要掌握和使用大量DOM的API,当然我们可以通过jQuery来简化和适配一些API的使用,另外关于数据(状态),往往会分散到各个地方,不方便管理和维护;
他们就去思考,是否有一种新的模式来解决上面的问题:
- 以组件的方式去划分一个个功能模块;
- 组件内以jsx来描述UI的样子,以
state
来存储组件内的状态;
- 当应用的状态发生改变时,通过
setState
来修改状态,状态发生变化时,UI会自动发生更新。
(四)React特点
- 声明式编程
声明式编程是目前整个大前端开发的模式:Vue、React、Flutter、SwiftUI,它允许我们只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染我们的UI界面(
f
函数就是render
函数)
- 组件化开发 组件化开发页面目前前端的流行趋势,我们会讲复杂的界面拆分成一个个小的组件;如何合理的进行组件的划分和设计是一个重点;
- 多平台适配
- 2013年,React发布之初主要是开发Web页面;
- 2015年,Facebook推出了ReactNative,用于开发移动端跨平台;(虽然目前Flutter非常火爆,但是还是有很多公司在使用(ReactNative);
- 2017年,Facebook推出ReactVR,用于开发虚拟现实Web应用程序;(随着5G的普及,VR也会是一个火爆的应用场景);
掌握最先进的思想和技术
- React由Facebook来更新和维护,它是大量优秀程序员的思想结晶。React的流行不仅仅局限于普通开发工程师对它的认可,大量流行的其他框架借鉴React的思想。
- Vue.js框架设计之初,有很多的灵感来自Angular和React
- 包括Vue3很多新的特性,也是借鉴和学习了React。比如React Hooks是开创性的新功能,Vue Function Based API学习了React Hooks的思想
- Flutter的很多灵感都来自React,来自官网的一段话:(SwiftUI呢) 事实上Flutter中的Widget – Element – RenderObject,对应的就是JSX – 虚拟DOM – 真实DOM
- 所以React可以说是前端的先驱者,它总是会引领整个前端的潮流。
二、牛刀小试
(一)Hello React案例——原生实现
我们用不同的方法实现在界面显示一个文本:Hello World,点击下方的一个按钮,点击后文本改变为Hello React。
⏺ 原生开发(命令式编程)
命令式编程:每做一个操作,都是给计算机(浏览器)一步步命令
(二)Hello React案例——引入React
⏺ React开发
开发React必须依赖三个库:
- react:包含react所必须的核心代码
- react-dom:react渲染在不同平台所需要的核心代码
- babel:将jsx转换成React代码的工具
第一次接触React会被它繁琐的依赖搞蒙,对于Vue来说,我们只是依赖一个vue.js文件即可,但是react居然要依赖三个库。
- 其实呢,这三个库是各司其职的,目的就是让每一个库只单纯做自己的事情
- 在React的0.14版本之前是没有react-dom这个概念的,所有功能都包含在react里。
- 为什么要进行拆分呢?原因就是react-native(进行移动端开发)。
- react包中包含了react和react-native所共同拥有的核心代码。
react-dom针对web和native所完成的事情不同:
- web端:react-dom会讲jsx最终渲染成真实的DOM,显示在浏览器中
- native端:react-dom会讲jsx最终渲染成原生的控件(比如Android中的Button,iOS中的UIButton)。
Babel ,又名Babel.js,是目前前端使用非常广泛的编辑器、转移器。比如当下很多浏览器并不支持ES6的语法,但是确实ES6的语法非常的简洁和方便,我们开发时希望使用它。那么编写源码时我们就可以使用ES6来编写,之后通过Babel工具,将ES6转成大多数浏览器都支持的ES5的语法。
React和Babel的关系:
- 默认情况下开发React其实可以不使用babel。
- 但是前提是我们自己使用
React.createElement
来编写源代码,它编写的代码非常的繁琐和可读性差。
- 那么我们就可以直接编写jsx(JavaScript XML)的语法,并且让babel帮助我们转换成
React.createElement
。
所以,我们在编写React代码时,这三个依赖都是必不可少的。添加这三个依赖的方式:
- 方式一:直接CDN引入
- 方式二:下载后,添加本地依赖
- 方式三:通过npm管理(后续脚手架再使用)
以上代码点击按钮message发生了修改,但是并没有渲染到页面上,所以我们需要再调用render函数进行重新渲染,具体代码如下:
(三)Hello React案例——组件化实现
有两个重点,第一个是按钮绑定事件需要加上
.bind(this)
,第二个是需要实现响应式开发的变量必须要放在 state
中,并通过 setState()
进行赋值(四)Js基础补充
1. 类的定义
以下分别从ES5和ES6的两个标准说明对类的定义
2. 类的继承
三、JSX核心语法
(一)电影列表案例
(二)计数器案例
(三)认识JSX
上述element变量的声明右侧赋值的标签语法,即是JSX语法,其需要依赖babel插件和
type="text/babel"
,二者缺一不可。JSX是什么?
- JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法;
- 它用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用;
- 它不同于Vue中的模块语法,你不需要专门学习模块语法中的一些指令(比如v-for、v-if、v-else、v-bind);
React选择了JSX的原因(All in js)
React认为渲染逻辑本质上与其他UI逻辑存在内在耦合,比如UI需要绑定事件(button、a原生等等);比如UI中需要展示数据状态,在某些状态发生改变时,又需要改变UI。
他们之间是密不可分,所以React没有讲标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件(Component)
书写规范:
- JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div原生(或者使用后面我们学习的Fragment);
- 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写,⚠️ 如果不用小括号,需要全部放在一行;
- JSX中的标签可以是单标签(必须以
/>
结尾),也可以是双标签;
(四)JSX使用
1. JSX注释
JSX的注释是
{/* 我是一段注释 */}
2. JSX嵌入数据
- 情况一:当变量是Number、String、Array类型时,可以直接显示
- 情况二:当变量是null、undefined、Boolean类型时,内容为空;
- 如果希望可以显示null、undefined、Boolean,那么需要转成字符串;
- 转换的方式有很多,比如toString方法、和空字符串拼接,String(变量)等方式;
- 情况三:对象类型不能作为子元素(not valid as a React child)
3. JSX嵌入表达式
主要分为三种:
- 运算表达式
- 三元运算符
- 执行一个函数
4. JSX绑定属性
例如以下属性:
- 比如元素都会有title属性
- 比如img元素会有src属性
- 比如a元素会有href属性
- 比如元素可能需要绑定class
- 比如原生使用内联样式style(属性名使用驼峰命名)
5. JSX事件绑定
原生DOM原生有一个监听事件,我们可以有两种办法:其一是获取DOM原生,添加监听事件;其二是在HTML原生中,直接绑定
onclick
;在React中操作:React 事件的命名采用小驼峰式(camelCase),而不是纯小写。我们需要通过
{}
传入一个事件处理函数,这个函数会在事件发生时被执行;关于以下代码中
this
的问题我们这里相关函数中打印
this
,也会发现它是一个 undefined
。原因是 btnClick()
函数并不是我们主动调用的,而且当button发生改变时,React内部调用了 btnClick()
函数;解决
this
的问题方案一:bind给btnClick显示绑定this
方案二:使用ES6 class fields 语法
方案三:事件监听时传入箭头函数(推荐)
6. JSX传递参数
在执行事件函数时,有可能我们需要获取一些参数信息:比如event对象、其他参数。
情况一:获取event对象,很多时候我们需要拿到event对象来做一些事情(比如阻止默认行为),假如我们用不到this,那么直接传入函数就可以获取到event对象(即函数默认的参数即为
event
)情况二:获取更多参数。有更多参数时,我们最好的方式就是传入一个箭头函数,主动执行的事件函数,并且传入相关的其他参数;
浏览器原生的点击事件操作如下:
在React中也有类似的事件,但是他们并不是同一种类型的对象,它是React内部合成的对象
7. 条件渲染
某些情况下,界面的内容会根据不同的情况显示不同的内容,或者决定是否渲染某部分内容。
在Vue中,我们会通过指令来控制:比如
v-if
、v-show
;在React中,所有的条件判断都和普通的JavaScript代码一致;- 方式一:条件判断语句(适合逻辑较多的情况)
- 方式二:三元运算符(适合逻辑比较简单)
- 与运算符&&(适合如果条件成立,渲染某一个组件;如果条件不成立,什么内容也不渲染)
- v-show的效果(主要是控制
display
属性是否为none
)
以下是仿照Vue,
v-show
效果的实现,通过控制css的 display
属性,控制元素的显示和隐藏8. 列表渲染
真实开发中我们会从服务器请求到大量的数据,数据会以列表的形式存储:比如歌曲、歌手、排行榜列表的数据;比如商品、购物车、评论列表的数据;比如好友消息、动态、联系人列表的数据;
以下有一些函数较为常用:
map()
用于展示列表、filter()
用于过滤列表数据、slice()
用于数据的截取。9. JSX的本质
实际上,jsx 仅仅只是
React.createElement(component, props, ...children)
函数的语法糖,所有的jsx最终都会被转换成 React.createElement
的函数调用。如果采用
React.createElement()
创建元素,则可以省略babel插件和script标签的类型限制,具体代码如下接下来我们从源码的角度对
createElement()
函数进行解读createElement()
需要传递三个参数:- 参数一:type当前ReactElement的类型; 如果是标签元素,那么就使用字符串表示“div”; 如果是组件元素,那么就直接使用组件的名称;
- 参数二:config 所有jsx中的属性都在config中以对象的属性和值的形式存储
- 参数三:children 存放在标签中的内容,以children数组的方式进行存储; 当然,如果是多个元素呢?React内部有对它们进行处理,处理的源码在下方
10. Babel转换
babel的官网中提供快速查看转换的过程,👉 官网
看到Babel转化后的代码我们可以看到,
React.createElement()
函数的参数不止三个,但是函数定义只有三个,实际上背后的逻辑如下:然后我们需要验证结果,我们将转化后的代码进行运行
11. JSX与虚拟DOM
我们通过
React.createElement()
最终创建出来一个 ReactElement 对象(可以见上源码)。ReactElement对象是用于组成一个JavaScript的对象树,对象树就是大名鼎鼎的虚拟DOM(Virtual DOM),然后通过
render()
函数将虚拟DOM树转化成真实DOM树。💡 jsx → createElement函数 → ReactElement(对象树) → ReactDOM.render→ 真实DOM
查看ReactElement的树结构的方法如下:
💡 真实DOM和虚拟DOM对比:
- 很难跟踪状态发生的改变:原有的开发模式,我们很难跟踪到状态发生的改变,不方便针对我们应用程序进行调试;
- 操作真实DOM性能较低:传统的开发模式会进行频繁的DOM操作,而这一的做法性能非常的低;
E.g. 我们有一组数组需要渲染:
[0, 1, 2, 3, 4]
,我们可以用 ul
和 li
将他们展示出来后来我们增加5条数据,即
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
,我们要么通过重新遍历整个数组(不推荐),要么在后面追加另外5个元素的 li
。以上代码性能非常低效,因为我们通过
document.createElement
创建元素,再通
过 ul.appendChild(li)
渲染到DOM上,进行了多次DOM操作;对于批量操作的,最好的办法不是一次次修改DOM,而是对批量的操作进行合并;(比如可以通过DocumentFragment进行合并);我们可以通过Virtual DOM来帮助我们解决上面的问题;
所以虚拟DOM帮助我们从命令式编程转到了声明式编程的模式。根据React官方的说法:Virtual DOM 是一种编程理念。在这个理念中,UI以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的JavaScript对象。我们可以通过
ReactDOM.render
让虚拟DOM 和真实DOM同步起来,这个过程中叫做协调(Reconciliation);这种编程的方式赋予了React声明式的API,只需要告诉React希望让UI是什么状态,React来确保DOM和这些状态是匹配的,不需要直接进行DOM操作,可以从手动更改DOM、属性操作、事件处理中解放出来。
(五)购物车案例
1. 练习要求
- 在界面上以表格的形式,显示一些书籍的数据;
- 在底部显示书籍的总价格;
- 点击+或者-可以增加或减少书籍数量(如果为1,那么不能继续-);
- 点击移除按钮,可以将书籍移除(当所有的书籍移除完毕时,显示:购物车为空~);
2. 页面结构
首先通过render函数编写大概的界面布局结构
3. 总价格显示
4. 移除书籍和购物车为空
5. 书籍数量修改
- 作者:😈Zabanya
- 链接:https://blog.zabanya.space//article/46e3eb06-e3e9-4376-a38d-19ca7edfd9d4
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处