当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 浅谈从React渲染流程分析Diff算法

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

2019年06月03日  | 移动技术网IT编程  | 我要评论

戴永湘,波伦尼特,非常变身奏鸣曲

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生命周期图

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

class tab extends react.component {
 render() {
 react.createelement(
  'p',
  { classname: 'class'},
  'hello react'
 )
 }
}

通过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;
};

打印出组件:

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算法即将更新,敬请期待~~~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网