浅谈从React渲染流程分析Diff算法


Posted in Javascript onSeptember 08, 2018

React中最神奇的部分莫过于虚拟DOM,以及其高效的Diff算法。这让我们可以无需担心性能问题而”毫无顾忌”的随时“刷新”整个页面,由虚拟DOM来确保只对界面上真正变化的部分进行实际的DOM操作。React在这一部分已经做到足够透明,在实际开发中我们基本无需关心虚拟DOM是如何运作的。然而,理解其运行机制不仅有助于更好的理解React组件的生命周期,而且对于进一步优化React程序也会有很大帮助。

1、什么是虚拟DOM

在React中,render执行的结果得到的并不是真正的DOM节点,结果仅仅是轻量级的JavaScript对象,我们称之为virtual DOM。

简单的说,其实所谓的virtual DOM就是JavaScript对象到Html DOM节点的映射;即使用JavaScript对象将Html结构表示出来,而这个对象就是virtual DOM。

eg:

Html:

<ul id='list'>
 <li class='item'>Item 1</li>
 <li class='item'>Item 2</li>
</ul>

JavaScript对象表示(virtual DOM)

{
 tagName: 'ul',
 props: {
 id: 'list'
 },
 children: [
 {tagName: 'li', props: {class: 'item'}, children: ["Item 1"]},
 {tagName: 'li', props: {class: 'item'}, children: ["Item 2"]},
 ]
}

2、什么时候会生成到virtual DOM

React生命周期拥有装载、更新、卸载的三个阶段;附上一张React生命周期图

浅谈从React渲染流程分析Diff算法

前面提到:render执行的结果得到的并不是真正的DOM节点,结果仅仅是轻量级的JavaScript对象,即在render函数调用时将会创建出虚拟DOM;

class Tab extends React.Component {
 render() {
 React.createElement(
  'p',
  { className: 'class'},
  'Hello React'
 )
 }
}

浅谈从React渲染流程分析Diff算法

通过React.createElemen创建出虚拟DOM,而该函数只在Render函数中调用,所以在React装载和更新的过程中才会有虚拟DOM的生成;至于挂载到真实DOM自然而然是ReactDom.render函数啦。

3、virtual DOM如何实现

实现其实很简单,主要是定义一个函数并把我们传进去的参数组成一个React元素对象,而type就是我们传进去的组件类型,可以是一个类、函数或字符串(如'div')

React大致源码:

function createElement(type, config, children) {
 let propName;

 const props = {};

 let key = null;
 let ref = null;
 let self = null;
 let source = null;

 if (config != null) {
 if (hasValidRef(config)) {
 // 如果有ref,将它取出来
 ref = config.ref;
 }
 if (hasValidKey(config)) {
 // 如果有key,将它取出来
 key = '' + config.key;
 }

 self = config.__self === undefined ? null : config.__self;
 source = config.__source === undefined ? null : config.__source;
 
 for (propName in config) {
 if (
 hasOwnProperty.call(config, propName) &&
 !RESERVED_PROPS.hasOwnProperty(propName)
 ) {
 // 将除ref,key等这些特殊的属性放到新的props对象里
 props[propName] = config[propName];
 }
 }
 }

 // 获取子元素
 const childrenLength = arguments.length - 2;
 if (childrenLength === 1) {
 props.children = children;
 } else if (childrenLength > 1) {
 const childArray = Array(childrenLength);
 for (let i = 0; i < childrenLength; i++) {
 childArray[i] = arguments[i + 2];
 }
 props.children = childArray;
 }

 // 添加默认props
 if (type && type.defaultProps) {
 const defaultProps = type.defaultProps;
 for (propName in defaultProps) {
 if (props[propName] === undefined) {
 props[propName] = defaultProps[propName];
 }
 }
 }
 
 return ReactElement(
 type,
 key,
 ref,
 self,
 source,
 ReactCurrentOwner.current,
 props,
 );
}

const ReactElement = function(type, key, ref, self, source, owner, props) {
 // 最终得到的React元素
 const element = {
 // This tag allows us to uniquely identify this as a React Element
 $$typeof: REACT_ELEMENT_TYPE,

 // Built-in properties that belong on the element
 type: type,
 key: key,
 ref: ref,
 props: props,

 // Record the component responsible for creating this element.
 _owner: owner,
 };

 return element;
};

打印出组件:

浅谈从React渲染流程分析Diff算法

4、为什么需要使用virtual DOM

DOM管理历史阶段:

  1. JS 或者 jQuery 操作 DOM: 当应用程序越来越复杂,需要在JS里面维护的字段也越来越多,需要监听事件和在事件回调用更新页面的DOM操作也越来越多,应用程序会变得非常难维护。
  2. 后来产出 MVC、MVP 的架构模式,期望从代码组织方式来降低维护难度。但是 MVC 架构并没办法减少维护的状态,也没有降低状态更新时需要对页面的更新操作,你需要操作的DOM还是需要操作,只是换了个地方。
  3. 既然状态改变了要操作相应的DOM元素,为什么不做一个东西让视图和状态进行绑定,状态变更了视图自动变更。这就是后来人们想出了 MVVM 模式,只要在模版中声明视图组件是和什么状态进行绑定的,双向绑定引擎就会在状态更新的时候自动更新视图;
  4. 但MVVM双向数据绑定并不是唯一的办法,还有一个非常直观的方法:一旦状态发生了变化,就用模版引擎重新渲染整个视图,然后用新的视图更换掉旧的视图。

React采用的就是第四种模式;但是我们都知道对于操作DOM成本太高,而相对操作JavaScript就快速多了,而Html DOM可以很简单的用JavaScript对象表示出来(Virtual DOM就这样诞生了)

这样的做法会导致很多的问题,最大的问题就是这样做会很慢,因为即使一个小小的状态变更都要重新构造整棵 DOM,性价比太低;而React Virtual DOM在状态更新过程加了一些特别的操作来避免整棵 DOM 树变更(它就是接下来的Diff算法)。

接下来的Diff算法即将更新,敬请期待~~~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Prototype使用指南之enumerable.js
Jan 10 Javascript
用javascript控制iframe滚动的代码
Apr 10 Javascript
js输入框邮箱自动提示功能代码实现
Dec 10 Javascript
js中对象的声明方式以及数组的一些用法示例
Dec 11 Javascript
JS+CSS实现的蓝色table选项卡效果
Oct 08 Javascript
JavaScript 动态三角函数实例详解
Jan 08 Javascript
JS实现的随机排序功能算法示例
Jun 09 Javascript
JavaScript中三个等号和两个等号你了解多少
Jul 04 Javascript
JS 使用 window对象的print方法实现分页打印功能
May 16 Javascript
微信小程序实现天气预报功能
Jul 18 Javascript
js隐式转换的知识实例讲解
Sep 28 Javascript
javascript中Set、Map、WeakSet、WeakMap区别
Dec 24 Javascript
详解Webpack-dev-server的proxy用法
Sep 08 #Javascript
详解Ubuntu安装angular-cli遇到的坑
Sep 08 #Javascript
JavaScript实现JSON合并操作示例【递归深度合并】
Sep 07 #Javascript
Bootstrap-table使用footerFormatter做统计列功能
Sep 07 #Javascript
jQuery实现为动态添加的元素绑定事件实例分析
Sep 07 #jQuery
Bootstrap-table自定义可编辑每页显示记录数
Sep 07 #Javascript
vue.js 双层嵌套for遍历的方法详解, 类似php foreach()
Sep 07 #Javascript
You might like
php _autoload自动加载类与机制分析
2012/02/10 PHP
php 批量替换程序的具体实现代码
2013/10/04 PHP
PHP中的排序函数sort、asort、rsort、krsort、ksort区别分析
2014/08/18 PHP
php实现按照权重随机排序数据的方法
2015/01/09 PHP
PHP中如何使用session实现保存用户登录信息
2015/10/20 PHP
Laravel 5.4因特殊字段太长导致migrations报错的解决
2017/10/22 PHP
Thinkphp5+uploadify实现的文件上传功能示例
2018/05/26 PHP
JS提交并解析后台返回的XML的代码
2008/11/03 Javascript
FireFox与IE 下js兼容触发click事件的代码
2008/11/20 Javascript
AJAX分页的代码(后台asp.net)
2011/02/14 Javascript
JQuery中对服务器控件 DropdownList, RadioButtonList, CheckboxList的操作总结
2011/06/28 Javascript
js模拟滚动条(横向竖向)
2013/02/22 Javascript
javascript获取隐藏元素(display:none)的高度和宽度的方法
2014/06/06 Javascript
javascript实现表格排序 编辑 拖拽 缩放
2015/01/02 Javascript
谈谈因Vue.js引发关于getter和setter的思考
2016/12/02 Javascript
Node.js设置CORS跨域请求中多域名白名单的方法
2017/03/28 Javascript
javaScript中封装的各种写法示例(推荐)
2017/07/03 Javascript
使用npm安装最新版本nodejs
2018/01/18 NodeJs
layer.open关闭父窗口 以及调用父页面的方法
2018/08/17 Javascript
JS实现可视化文件上传
2018/09/08 Javascript
分享5个小技巧让你写出更好的 JavaScript 条件语句
2018/10/20 Javascript
vue无限轮播插件代码实例
2019/05/10 Javascript
原生js通过一行代码实现简易轮播图
2019/06/05 Javascript
vant-ui AddressEdit地址编辑和van-area的用法说明
2020/11/03 Javascript
python实现代理服务功能实例
2013/11/15 Python
python自然语言编码转换模块codecs介绍
2015/04/08 Python
详解Python编程中基本的数学计算使用
2016/02/04 Python
python实现搜索本地文件信息写入文件的方法
2016/02/22 Python
pyenv虚拟环境管理python多版本和软件库的方法
2019/12/26 Python
css3中单位px,em,rem,vh,vw,vmin,vmax的区别及浏览器支持情况
2016/12/06 HTML / CSS
HTML5 video视频字幕的使用和制作方法
2018/05/03 HTML / CSS
中国跨境电子商务网站:NewFrog
2018/03/10 全球购物
大学教师年终总结的自我评价
2013/10/29 职场文书
淘宝中秋节活动方案
2014/01/31 职场文书
辞职信怎么写
2015/02/27 职场文书
MySQL 发生同步延迟时Seconds_Behind_Master还为0的原因
2021/06/21 MySQL