3

React原理学习

React出身FaceBook豪门,一出生就带着virtualDOM和diff算法两大颠覆式的被动技能,很快引来高度关注,并且以高效快速著称。
经过一段时间的使用,抽出时间来学习其原理,发现并不难,但是想法很创新。造就了其不同于传统框架的开发模式。

一、VirtualDOM

VirtualDOM一直是React最著名,最神秘的特点。也是其高效迅速的核心。
virtualDOM其实很好理解:FaceBook的工程师认为传统的框架,频繁的操作DOM代价很大。性能和速度很低,想找一种东西替代DOM,但又能有DOM的嵌套结构和属性。于是FaceBook工程师们创造出virtualDOM。本质上virtualDOM就是一个json对象,用来描述真实DOM应有的属性和子集。拥有和DOM一样的嵌套关系形成的virtualDOM树。

举个例子:我们有一段HTML

<div id="wrap">
    <div class="box1" style="margin-left: 10px;padding-top: 10px;"></div>
    <div class="box2">
        <span data-name='box'>this is a box</span>
    </div>
</div>

上述是一段很简单的有嵌套关系的HTML,我们可以看到里面有几个关键属性,其实只需要记住这几个关键属性,我们就相当于知道了整段节点。
根据上述HTML,抽象出来的该节点的关键属性。

var conatiner = {
    tagName: 'div',
    id: 'wrap',
    children: [box1, box2]
};

var box1 = {
    tagName: 'div',
    className: 'box1',
    style: {marginLeft: '10px', paddingTop: '10px'},
    children: []
}

var box2 = {
    tagName: 'div',
    className: 'box2',
    children: [text]
};

var text = {
    tagName: 'span',
    'data-name': 'box', // 自定义属性
    text: 'this is a box'
    children: []
};

上面的HTML里面真正有用的信息仅仅几行代码就可以描述。React做的其实就是反向绘制。

一个真实节点有很多属性,但是很多属性是绘制DOM相关的(比如offset,scrollTop这种,节点渲染之后添加的跟节点样式相关的属性)。virtualDOM对这些不常见属性暂不考虑(实际上这些绘制DOM的属性都在react-dom中),抽出重要的属性,以及嵌套关系记录下来(高效),放到内存中(迅速),这样减少了很多用不到的属性。 当virtualDOM树生成出来之后,只要按照特地的方法解析(JSX),就会快速的生成出DOM了。
以上大概就是React诞生的原因和核心原理。

二、diff算法

另一个著名的特点就是神秘的diff算法。这个算法也不难,甚至觉得并不如宣传的那么牛*还有些失望。但是还是比较麻烦的。
diff算法主要是用来判断两个virtualDOM树差异的,目的是以最小的代价去更新树。
当特定情境发生需要更改DOM的时候,React并不认为直接操作DOM是最好的选择。他认为无论是简单的DOM操作还是复杂的DOM操作,都应由改动前的virtualDOM和改动后的virtualDOM计算出来,然后根据计算结果再去渲染DOM。这样相当于是计算出每次操作DOM的最优算法
举个例子 需要移动火材棍使等式成立

clipboard.png

图1-移动火材棍使等式成立示意图

普通情况下大家直接将4个火材棍移到最前,两个火材棍移到中间,这样4-2=2等式成立。
聪明一点的人发现只需要移动中间的两个火材棍到最左,即可使等式成立。这样代价更小。
React经过计算发现:原来只需要移动"="的一个火材棍到"-"即可使等式成立2=4-2这样代价最小。
diff算法就是这样的一套标准化计算流程,其告诉React每次变化如何更改DOM代价最小,或者直接重新渲染性能最优。

三、State

React的另一个创新思维就是数据为中心。这也是现代前端框架普遍的思想。
现在渲染DOM也不用我们来管了,操作DOM也不用我们来管了那我们来管什么?
我们需要管理数据,告诉React什么使用,应该有什么样的DOM。
React是根据virtualDOM来渲染DOM的,那virtualDOM是根据什么生成的?答案就是state
React关键点就是state改变,就会重新渲染DOM,这样只需要将影响页面的数据放到state中。设置好何时如何触发修改state,剩下的React就会自动完成。

四、JSX

JSX可以说也是React的核心部分。它使用XML的标记方式直接去声明界面,组件界面间可以嵌套。
JSX最鲜明的特征就是,script标签中声明text属性为text/babel,这样调用babel解释器。
上面是官方的说法用通俗的话说就是:JSX其实就是一个为了使我们方便使用React发明的一种转义工具。
包括在下面的例子中,我们可以看到例子中的语法跟正常使用的语法不同,就是因为babel。React原始的语法是不支持我们现在使用组件嵌套的,比如down下React官方推荐的demo,第二个例子中的源代码如下:

clipboard.png

图2-使用JSX语法编写示例

可以看到Counter组件的render方法return出了一个button组件。
如果打开React官方demo的第一个例子,会发现语法完全不同。

clipboard.png

图3-使用React语法编写示例

可以看到原始的React语法是不支持组件嵌套的。甚至是不支持return <div><div>的。render方法return的只能是虚拟DOM。

五、React渲染过程研究

React如何将用户输入的代码实现成真实DOM,是接下来要研究的问题。大致过程可以分为首次渲染和再次渲染。

clipboard.png

图4-React渲染机制示意图

上图是整理出来的React渲染机制的流程图,左边的首次渲染的过程,右边是再次渲染的过程。这里主要讲解下该图。

1.首次渲染

我们在使用React的时候,往往定义很多组件,组件输出的DOM中,还有自定义组件。
在JS最后,使用ReactDOM.render(element, container);将定义好的组到一个容器中。
我们可以将首次渲染过程分为以下几步:

  1. React首先对我们定义组件进行解析 解析后的每个组件的返回结果其实就是一个个virtualDOM 每个virtualDOM中都会有children属性保存其子节点 这样根节点解析后 会产生一个完整的virtualDOM树。

  2. React.render解析根节点virtualDOM 进而解析整个virtualDOM树(在React1.0之后 React拆分成React和React-dom 现在都使用ReactDOM.render)

  3. React.render将virtualDOM传递给组件工厂 工厂判定virtualDOM具体类别进行具体解析 比如React将virtualDOM分为三类:文本节点 浏览器自带元素和自定义组件 每个类的virtualDOM都有各自对应的component类进行解析 如上图

  4. 将每个virtualDOM解析之后 调用各自component类的mountComponent方法 将virtualDOM翻译成真实DOM 并将其事件挂载到Document对象上

  5. 将整个virtualDOM翻译的真实DOM挂载到容器中

  6. 触发mount事件

这样就完成了首次渲染的逻辑,可以看到首次渲染的逻辑还是比较简单,就是整理出定义的关键属性和嵌套关系成virtualDOM,按照virtualDOM进行绘制真实DOM,输出到挂载点上。
可以看一个简单的例子:

clipboard.png
图5-React首次渲染实例-代码

clipboard.png
图6-React首次渲染实例-显示

在上面的例子中我们可以看到,定义了一个block和Box。React.createElement的第三个参数就是其子节点数组,box有三个block子节点和一个文本节点。最后将输出的DOM挂载到container上,显示如预期一样。我们再来看看在此渲染的过程:

2.再次渲染(reRender)

  1. 每次调用setState方法,都会调用该组件receiveComponent方法。

  2. receiveComponent方法修改state并且判断,更改state后DOM是否发生修改,是的话diff算法实现最小更新。

  3. 渲染完成 调用hook函数componentDidUpdate。

这里再次渲染过程很简单,但是是最复杂的过程。每次渲染完成时virtualDOM树会继续保存在内存中, 在更改state等情况下直接使用。 三种不同类型的节点,有着不同的更改方法:比如文本节点直接替换 浏览器自带元素使用diff算法复杂计算。
React节点会有一个key属性,该属性就是React进行diff计算的。key上携带者嵌套,索引等相关信息。 diff算法中会递归找出virtualDOM树中的差别,组装成差异对象,添加到更新队列,在合适的时机进行更新。
以上就是React渲染机制的大致过程,至于diff算法这里暂不介绍,下面参考链接有详细介绍。

React优势和劣势

2013年React开源,作为目前较火的现代前端框架它有哪些优点和缺点,也是我们可以了解。在众多框架中选型的重要参考。
目前较流行的框架有angular、vue等,下面横向作对比分析其优缺点。

1. Angular

Angular是09年google工程师创建的。作为划时代的前端框架,其创新性的使用指令系统和MVVM框架
优点:
丰富的指令系统,强大的指令系统是的开发者可以有很多方便的使用方法供选择;
较完善的MVVM框架,包含模板、路由、双向数据绑定、模块化、服务、过滤器、依赖注入等所有功能;
google去维护,用户数多,社区强大。
缺点:
太过繁杂的指令系统,提高了学习成本。
渲染速度太慢,有人做过测试如下图:

clipboard.png
图中表示不同框架每秒能渲染的操作数,越长代表性能越好。可见angular基本排在最后。
angular2对比angular1断崖式升级,对开发者造成很高的学习成本。

2. Vue

Vue是近些年最火爆的框架(国内)。尤其在作者尤大加入到阿里后,vue在国内的影响力和推广力更强。
优点:
渲染效率差不多是React的10倍;
结合Angular和React的优点,有模板、有virtualDOM、有过滤器和指令系统。上手简单,十分方便。
缺点:
新生儿。作者16加入阿里,维护和社区方面跟其他两个框架比,不是很强大。
不支持IE8。(??什么是IE8??)

3. React

主角来了。经过几年的发展,大家的实践,React也是越来越完善。不断推出的React邪教全家桶(router redux RN等)也是渐渐丰富了React的生态。
优点:
思想新颖,影响了vue等框架;
FaceBook是他亲爹,很多大型项目都在用,完善的维护和强大的社区(国外各种追捧)。
缺点:
新颖的思维,造成了其需要理解实现过程,上手成本较高;
对动画效果支持不是很好;
不是MV框架,只负责UI绘制需要学习全家桶来实现MV框架。

参考文献:

  1. reactjs源码分析-上篇(首次渲染实现原理)

  2. reactjs源码分析-下篇(更新机制实现原理)

  3. Angular Vue React 渲染实现比较

  4. ARV 渲染实现比较之 Angular

  5. 对比其他框架 - Vue.js

  6. vue,angular,avalon这三种MVVM框架之间有什么优缺点?


Aus0049
2.4k 声望231 粉丝

console.log(([][[]]+[])[+!![]]+([]+{})[!+[]+!![]])