美文网首页
基于React理解虚拟DOM(Virtual DOM)和Diff

基于React理解虚拟DOM(Virtual DOM)和Diff

作者: 雪燃归来 | 来源:发表于2020-06-19 13:24 被阅读0次

           我在之前的文章《虚拟DOM(Virtual DOM)中动态更新视图的diff算法》中,基于vue描述Virtual DOM(下面可以称为vm)和Diff算法。由于vue2.X中的vm是建立在第三方库snabbdom之上的,所以我们通过分析了snabbdom的关键源码片段讲述了其中几个重要方法。如createElement、render、patch等,如果你感兴趣,可以点击文中的链接进行阅读。
           在react中,vmdiff算法有很大的相似之处,本篇文章主要从逻辑上进行描述,并不深入源码去分析。
           首先,我们需要清楚一个概念,那就是render函数的执行时机。简单一句话就可以概括。

    当组件的state或者props发生改变的时候,render函数就会重新执行

    一、几种视图渲染的设计方案

           下面是一个很简单的React组件的代码。从代码中我们可以看出,React组件有state(数据)JSX模版组成。那么要从数据+模版到最终生成页面的DOM,怎么样设计才是最高效、性能最有的技术方案呢。

    import React, { Component } from 'react'
    
    class TodoItem extends Component{
        constructor(props){
            super(props)
        }
        
        render(){
            return (<li onClick={this.handleClick.bind(this)}>
                    {this.props.content}
            </li>)
        }
        handleClick(){
            this.props.deleteItem(this.props.index)
        }
    }
    
    export default TodoItem
    

    1、直接渲染成真实DOM

           这是一种最简单,最直接,也是最容易被想到的一种方法。具体的流程如下:
           1、state数据
           2、JSX模版
           3、数据+模版 结合,生成真实DOM来显示
           4、state发生改变
           5、数据 + 模版 结合,生成真实DOM,替换原来的DOM

    缺陷
           1、第一次生成一个完整的DOM片段
           2、第二次生成一个完整的DOM片段
           3、第二次的DOM替换第一次的DOM,非常耗性能

    2、对比文档碎片(DocumentFragment)修改已发生改变的DOM片段

           这种方法是通过对比前后两次生成DOM的不同(对比时,第二次生成的文档碎片),来修改仅仅有发生改动的DOM片段。具体的流程如下:
           1、state数据
           2、JSX模版
           3、数据+模版 结合,生成真实DOM来显示
           4、state发生改变
           5、数据+模版 结合,生成真实DOM,并不直接替换原始的DOM
           6、新的DOM(DocumentFragment)和原始的DOM做对比,找差异
           7、找到变化(如input的值
           8、只用新的DOM中的input元素,替换掉老的DOM中的input元素

    缺陷
           虽然减少了DOM更新的体积,但是增加了DOM比较的操作,所以性能提升不明显

    3、采用虚拟DOM和Diff算法

           这种方法是reactvue所采用的渲染方法,主要是通过用JS对象模拟原生DOM,进行一系列的对比、更新操作。由于JS的执行效率很高,再加上Diff算法的巧妙设计,使得这种方法效率也非常高,已成为前端世界中的明星。具体的步骤如下:
           1、state数据
           2、JSX模版
           3、生成虚拟DOM(虚拟DOM就是一个JS对象,用它来描述真实DOM)(\color{red}{损耗性能}

    ['div',{id: 'abc'},['span', {}, 'hello world']]
    

           4、用虚拟DOM生成真实DOM,来显示。

    <div id="abc"><span>hello world</span></div>
    

           5、state发生改变
           6、数据+模版 生成新的虚拟DOM(\color{green}{极大提升性能}
           7、比较原始虚拟DOM和新的虚拟DOM的区别,找到区别是span中的内容(\color{green}{极大提升性能}
           8、直接操作DOM,改变span中的内容

           我们需要注意下面几点
           1、JSX并不是虚拟DOM,只是模版,让我们编码更加简洁。
           2、render函数的作用主要是将虚拟DOM渲染成真实DOM,可以直接写html标签,也可以写虚拟DOM
    html标签

    render(){
        return <div><span>item</span></div>
    }
    

    虚拟DOM

    render(){
       return React.createElement('div', {}, React.createElement('span', {}, 'item'))
    }
    

    优点
           1、性能提升了。
           2、它使得跨端应用得以实现。React Native。vm在浏览器端生成DOM,在原生端生成对应平台所需要的组件。

    二、虚拟DOM中的Diff算法

           React中vm中的diff算法跟vue中diff算法原理相同。我们将从下面三点具体看一下Diff算法。

    1、React中异步更新数据

           在React中,我们不能直接对state中的数据进行操作,必须通过this.setState```这个方法进行操作,那么这样做有好处吗?肯定是有的。React 底层将改变多次state的改变合并为一次state的改变,减少了更新操作,提升了性能

    React 底层将改变多次state的改变合并为一次state的改变

    2、前后虚拟DOM对比时采用“同级比较”

           这个对比的方式跟vue中一致。从第一层开始对比,如果第一层没有改变(key和$el),则进行第二层的对比,依次往下。如果第一层发生了改变,直接用新的节点替换旧的节点。


    前后虚拟DOM对比时采用“同级比较”

    3、列表数据渲染时key值要固定

           在列表渲染的时候,我们要为每一条数据指定唯一的key值,并且这个key值要固定。如果没有key或者key值不确定的情况下,在diff算法前后对比的过程中,前后节点对照关系将不明确。无法做到只修改已经改变的节点,不改变没有发生变化的节点。所以我们不推荐使用循环时的索引值index来做key值。

    为每一条数据指定唯一的key值,并且这个key值要固定

           至此,文章结束,感谢您的阅读。

    相关文章

      网友评论

          本文标题:基于React理解虚拟DOM(Virtual DOM)和Diff

          本文链接:https://www.haomeiwen.com/subject/kgjvxktx.html