最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • jsliang 求职系列 - 37 - React - 虚拟 DOM

    正文概述 掘金(jsliang)   2020-12-09   495

    一 目录

    不折腾的前端,和咸鱼有什么区别

    目录
    一 目录二 前言三 虚拟 DOM 3.1 要点 1:浏览器渲染过程 3.2 要点 2:DOM 操作昂贵 3.3 要点 3:Diff 算法四 虚拟 DOM 实现原理五 虚拟 DOM 和真实 DOM 比对六 Diff 算法

    二 前言

    带着问题看文章:

    • 虚拟 DOM 是什么?
    • 虚拟 DOM 实现原理是什么?
    • Diff 是什么?

    三 虚拟 DOM

    jsliang 思路:通过 3 个要点讲解虚拟 DOM。

    1. 描述浏览器的渲染过程
    2. 真实 DOM 操作昂贵,所以需要虚拟 DOM
    3. Diff 简要做了什么,key 在当中扮演什么角色

    3.1 要点 1:浏览器渲染过程

    • 创建 DOM 树。用 HTML 解析器分析 HTML 元素,创建一棵 DOM 树。
    • 创建 CSS 规则树(CSS rule tree)。用 CSS 解析器解析 CSS 文件和 inline 样式,生成页面的样式表。
    • 创建 Render 树。将 DOM 树和 CSS 规则树关联起来,构建 Render 树。
    • 布局 Layout。根据 Render 树,浏览器开始布局,为每个 Render 树上的节点确定一个在显示器上出现的精确坐标。
    • 绘制 Painting。在 Render 树和节点显示坐标的基础上,调用每个节点的 paint 方法,将它们绘制出来。

    3.2 要点 2:DOM 操作昂贵

    由于在浏览器中操作 DOM 是很昂贵的:

    • 用原生 JS 或者 jQuery 操作 DOM 时,浏览器会从构建 DOM 树开始从头到尾执行一遍流程。

    频繁地操作 DOM,会产生一定的性能问题,因此我们需要这一层抽象,在 patch 过程中尽可能地一次性将差异更新到 DOM 中,这样保证了 DOM 不会出现性能很差的情况。

    但是这样并不能解决问题,所有就有了虚拟 DOM。

    虚拟 DOM 本质就是用一个原生的 JavaScript 对象去描述一个 DOM 节点,是对真实 DOM 的一层抽象。

    <div id="container">
      <ul>
        <li></li>
      </ul>
    </div>
    
    const tree = Element('div', { id: 'container' }, {
      Element('ul', {}, [
        Element('li', {}, ['新节点值'])
      ]),
    });
    
    const root = tree.render();
    document.querySelector('#container').appendChild(root);
    

    可以看到虚拟 DOM 对象最基本的三个属性:

    • 标签类型
    • 标签元素的属性
    • 标签元素的子节点

    3.3 要点 3:Diff 算法

    两棵树完全对比的时间复杂度是 O(n^3),而 React 的 Diff 算法的时间复杂度是 O(n)

    要实现这么低的时间复杂度,意味着在比较差异时只会对同一层级的节点进行比较,因为如果进行完全的比较,算法实际复杂度会过高,所以舍弃了这种完全的比较方式,而采用同层比较。

    Diff 算法的核心就是对虚拟 DOM 节点进行深度优先遍历,并对每一个虚拟节点进行编号,在遍历的过程中对同一个层级的节点进行比较,最终得到比较后的差异。

    假设现在的虚拟 DOM 的更新前后为:

    // DOM 节点值
    const dom = `
      <div id="container">
        <p>123</p>
        <ul>
          <li class="jsliang1">li 节点</li>
          <li>旧节点值</li>
        </ul>
      </div>
    `;
    
    // 旧节点
    const tree = Element('div', { id: 'container' }, [
      Element('p', {}, ['123']),
      Element('ul', {}, [
        Element('li', { class: 'jsliang1' }, ['li 节点']),
        Element('li', {}, ['旧节点值']),
      ]),
    ]);
    
    // 变成下面新的虚拟 DOM
    const tree = Element('div', { id: 'container' }, [
      Element('h3', {}, ['小标题']), // 更新节点
      Element('ul', {}, [
        Element('li', { class: 'jsliang2' }, ['li 节点']), // 更新属性或者属性值
        Element('li', {}, ['新节点值']), // 更新文本
      ]),
    ]);
    

    Diff 获取虚拟 DOM 节点变更的 4 种情况比较:

    • 节点类型变了<p> -> <h3>。直接 Replace,将旧节点卸载并装载新节点。
    • 节点类型一样,仅仅属性或者属性值变了。直接 Props,更新节点。
    • 文本变了。直接 Text,修改文字内容就行了。
    • 增加、删除或者移动了子节点。直接 Reorder,这个方法比较复杂,小伙伴们具体可以去了解下。

    Diff 的实现,最粗暴的方法就是遍历每个新虚拟 DOM 节点,和旧虚拟 DOM 节点比对。在旧 DOM 中是否存在,不同就卸载原来的上新的。

    这时候不得不提一下 React 或者 Vue 里面的 key,我们在写业务的时候,被告知 key 值不能是索引值 index

    为什么这样子呢?

    假设我们有 4 个元素,旧的元素是 1、2、3、4,新的元素是 1、3、2、4,但是如果我们用了索引值 index,那么它们就一直是 0-3。

    这样子的话,我们 React 或者 Vue 就没法比较好的监听它的一个变动。

    所以一般来说,我们将 key 值定位成数组的 id 或者其他值,方便它变动的时候去监听。

    这样子通过 Diff 比较完毕之后,我们就可以获取需要变动的内容,最终去更新真实 DOM 节点。

    四 虚拟 DOM 实现原理

    • 虚拟 DOM 本质上是 JavaScript 对象,是对真实 DOM 的抽象
    • 状态变更时,记录新树和旧树的差异
    • 最后把差异更新到真正的 DOM 中

    五 虚拟 DOM 和真实 DOM 比对

    优点:

    • 保证性能下限:虚拟 DOM 可以经过 Diff 找出最小差异,然后批量进行 patch,这种操作虽然比不上手动优化,但是比起粗暴的 DOM 操作性能要好很多,因此虚拟 DOM 可以保证性能下限。
    • 无需手动操作 DOM:虚拟 DOM 的 Diffpatch 都是在一次更新中自动进行的,我们无需手动操作 DOM,极大提高开发效率。
    • 跨平台:虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、移动端开发等。

    缺点:

    • 无法进行极致优化:在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化,例如 VS Code 采用直接手动操作 DOM 的方式进行极端的性能优化。

    六 Diff 算法

    比较原生虚拟 DOM 和新的虚拟 DOM 的区别,使用 Diff(Different)算法

    jsliang 求职系列 - 37 - React - 虚拟 DOM

    如上图,在 React 中,对于 setState,它采用异步操作,统一对 state 中的数据进行更改。


    jsliang 求职系列 - 37 - React - 虚拟 DOM

    首先,比对第一层的 DOM 节点,如果它相同,则往下继续对比;如果它不同,则停止对比,更新第一层及以下的 DOM 节点。

    然后,比对第二次的 DOM 节点……

    最后,形成一种比对算法。

    jsliang 求职系列 - 37 - React - 虚拟 DOM

    所以总结下来就是:

    • 把树形结构按照层级分解,只比较同级元素。
    • 给列表结构的每个单元添加唯一的 key 属性,方便比较。
    • React 只会匹配相同 classcomponent(这里面的 class 指的是组件的名字)
    • 合并操作,调用 componentsetState 方法的时候, React 将其标记为 dirty。到每一个事件循环结束, React 检查所有标记 dirtycomponent 重新绘制.
    • 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 Diff 的性能。


    起源地下载网 » jsliang 求职系列 - 37 - React - 虚拟 DOM

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元