Vue原理学习:vdom 和 diff算法(基于snabbdom)

vdom 和 diff

背景

基于组件化,数据驱动视图。只需关心数据,无需关系 DOM ,好事儿。

但是,JS 运行非常快,DOM 操作却非常慢,如何让“数据驱动视图”能快速响应?


引入 vdom

用 vnode 表示真实 DOM 结构

 <div id="div1" class="container">
     <p>vdom</p>
     <ul style="font-size: 20px">
         <li>a</li>
     </ul>
 </div>
 {
     tag: 'div',
     props: {
         className: 'container',
         id: 'div1'
     }
     children: [
         {
             tag: 'p',
             children: 'vdom'
         },
         {
             tag: 'ul',
             props: { style: 'font-size: 20px' }
             children: [
                 {
                     tag: 'li',
                     children: 'a'
                 }
                 // ....
             ]
         }
     ]
 }

演示 vdom 的使用(对比不用 vdom 的情况)—— snabbdom 和 jquery


使用 vdom 能快速操作 DOM

  • JS 执行很快

  • DOM 操作很慢

如何让 DOM 操作最快?—— 尽可能减少 DOM 操作,只操作需要更新的,不做多余操作。

如何尽量减少 DOM 操作?—— 两个 vnode 进行 diff ,找出不同。diff 是 JS 执行,会很快。 (画图示例,两棵 vnode ,找出不同)


diff 算法概述

diff 算法是一个很广泛的,前端常见的例如文本 diff ,json 对象 diff ,还有这里的“树 diff”。

diff 两棵树的时间复杂度是 O(n^3)(不可用的复杂度),例如 diff(Tree1, Tree2)

  • 遍历 Tree1 ,每个节点都要和 Tree2 对比

  • 针对 Tree1 的节点,遍历 Tree2 每个节点和它对比

  • 重新排序

但是,vdom diff 算法做了几个改进,让复杂度变为 O(n)

  • 只比较同一层级

  • tag 或组件不相同的,直接删掉重建,不再继续深入比较

  • tag 或组件 & key ,两个都相同的,即认为是相同节点


diff 算法过程详解

snabbdom https://github.com/snabbdom/snabbdom 是一款比较简洁、高性能的 vdom lib vue2.x 的 diff 算法完全参考它。 即了解 snabbdom 的 diff 算法,也就了解 vue2.x 的 diff 算法。应该面试的 diff 算法问题足够了

基本流程
  • 回顾一下它的基本使用,找出核心的 API: h patch

  • 下载 snabbdom 源码

  • 查看源码

注意
  • 解读源码,只看主干和要点,不要去扣细节

  • 源码是 ts ,但不妨碍我们阅读,不要关注语法细节

h 函数

【功能】h 函数是一个工厂函数,根据传入的参数,生成 vnode 结构

源码在 src/h.ts

输入和输出 function h(sel: string, data: VNodeData, children: VNodeChildren): VNode;

返回 return vnode(sel, data, children, text, undefined);

vnode 函数

源码在 src/vnode.ts

返回 return {sel, data, children, text, elm, key};

这里可以结合 demo 中的断点来看数据结构。此时的 elem 应该是 undefined

text 和 children

一个元素或者有 contentText ,后者有 children ,两者不能共存 demo 中有示例

patch 函数

【功能】:patch 函数将 newVnode 更新到 vnode 或者 elem 上,patch 的过程也就是 diff 的过程。

判断了老节点是否存在,然后执行销毁或者创建,然后执行 patchVnode

源码 src/snabbdom.ts ,找到其中的 init 函数,最后返回的就是 patch 函数。

输入输出 function patch(oldVnode: VNode | Element, vnode: VNode): VNode

(画图:elem 和 oldVnode vnode 的关系) (要考虑第一个参数是 VNode 和 Element 两种情况)

patchVnode 函数

patchVnode 函数是对比 两个虚拟 dom 不同的地方, 同时 也是 递归 调用 updateChildren 的 函数

源码在 src/snabbdom.ts

先看 addVnodesremoveVnodes ,最后看 updateChildren

updateChildren 函数

key的重要性

源码在 src/snabbdom.ts

以 todo list 的 items 变化,为例,图解演示即可


总结

diff 算法中,细节不是关键例如“头头 头尾 对比”等,核心概念才是关键,如 h vnode patch key 等。 所有的 diff 算法,以及无论如何做优化,都离不开这些核心概念

相关推荐

  1. vuediff算法

    2024-05-16 11:32:22       52 阅读
  2. vuereact的diff算法源码

    2024-05-16 11:32:22       45 阅读
  3. 深度学习基本原理算法

    2024-05-16 11:32:22       50 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-16 11:32:22       91 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-16 11:32:22       97 阅读
  3. 在Django里面运行非项目文件

    2024-05-16 11:32:22       78 阅读
  4. Python语言-面向对象

    2024-05-16 11:32:22       88 阅读

热门阅读

  1. 引导前端学习

    2024-05-16 11:32:22       27 阅读
  2. C++并发:构建线程安全的队列

    2024-05-16 11:32:22       32 阅读
  3. 由读写arrow引发的对时间时区的思考

    2024-05-16 11:32:22       31 阅读
  4. 17.定浮点数与有符号数

    2024-05-16 11:32:22       37 阅读
  5. 代码随想录算法训练营第七天|

    2024-05-16 11:32:22       26 阅读
  6. JVM

    2024-05-16 11:32:22       20 阅读
  7. 第二天:深入部署与配置

    2024-05-16 11:32:22       32 阅读
  8. Unity3D 打造3D Astar寻路系统详解

    2024-05-16 11:32:22       39 阅读