前端八股整理|Vue|虚拟 DOM、Diff 与 Patch 流程

文章目录

  • [前端八股整理|Vue|虚拟 DOM、Diff 与 Patch 流程](#前端八股整理|Vue|虚拟 DOM、Diff 与 Patch 流程)
  • [1.什么是虚拟dom?虚拟 dom 的好处?](#1.什么是虚拟dom?虚拟 dom 的好处?)
  • [2. diff算法](#2. diff算法)
    • [2.1 patch 函数](#2.1 patch 函数)
    • [2.2 patchVnode 函数](#2.2 patchVnode 函数)
    • [2.3 updateChildren函数](#2.3 updateChildren函数)

前端八股整理|Vue|虚拟 DOM、Diff 与 Patch 流程

1.什么是虚拟dom?虚拟 dom 的好处?

参考视频:Vue中的虚拟DOM 虚拟DOM如何使用?有什么好处?

jQuery时代是操作 Dom->视图更新

vue 时代是数据改变->操作 Dom->视图更新,但是从实际上执行来说 运行 js要比操作 dom 快的多得多,所以引入了虚拟 Dom

数据改变->虚拟 DOM(计算变更)->操作真实的 DOM->视图更新

什么是虚拟Dom?

就是用 JS 模拟 DOM 结构.虚拟 DOM 的三个关键组成是 tag,props,children.其中 tag 就是代表这个 html 的标签是什么,props 里面放一些 id,className 就看标签上有哪些属性,children 就是这个标签里面嵌套了哪些内容.

html 对应的虚拟 Dom

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app" class="container">
        <h1>虚拟DOM</h1>
        <ul style="color: red;">
            <li>第一项</li>
            <li>第二项</li>
        </ul>
    </div>
</body>
</html>
javascript 复制代码
{
  tag:'div',
  props:{
    id:'app',
    className:'container'
  },
  children:[
    {
      tag:'h1',
   		children:'虚拟 DOM'
    },
    {
      tag:'ul',
      props:{style="color:red"},
      children:[
        {
          tag:'li',
          children:'第一项'
				},
        {
          tag:'li',
          children:'第二项'
        }
      ]
    }
  ]
}

虚拟 dom 的好处:

比如更新 dom,虚拟dom 会计算一个最小的更新视图,再去操作 dom,比如有两个 dom 节点,现在修改 dom,第一项不变,第二项修改内容,第三项新增,使用虚拟 dom,最后只会更新第二项和第三项

2. diff算法

理论上,比较两棵树的最小差异是一个 O(n³) 的复杂问题(经典"树编辑距离"问题)。

对前端 DOM 来说,O(n³) 根本不可接受(n=1000 时已经算不动)。

Vue 采用了 启发式策略:只比较同层级节点,不做跨层比较 → 复杂度降低为 O(n)。

只比较同一层级

同层比较时

  • 标签名(tag)不同,直接删除,不继续深度比较
  • tag 相同但 key 不同:通常也认为不是同一个节点
  • 标签名(tag)相同,key相同,可以认为是相同节点,继续比较 props 和 children

虚拟 dom 节点 vnode 是怎么创建出来的?

typescript 复制代码
return {sel,data,children,text,elm,key}
  • sel:选择器,例如 div
  • data:比如样式
  • children:一个数组,包含了他的子节点
  • text:和 children 是对立的,两者只存在一个,text 就是纯文本
  • elm:就是要把这个节点渲染到某个真实的 dom 节点上的 dom 节点
  • key:v-for 提供的那个 key

2.1 patch 函数

patch 函数

首次页面渲染的时候去执行一次,目的是给他渲染到一个空的容器里来

javascript 复制代码
patch(container,vnode)

后面页面更新的时候,会把新的 vnode 去替换掉老的 vnode

javascript 复制代码
patch(vnode,newVnode)

patch 函数的过程

patch(oldVnode,vnode)

  • 首先就是如果传入的第一个参数不是 vnode,对应的就是首次渲染传入 container 的情况,此时就会创建空的 vnode,并关联 DOM元素,这样后续更新的时候就知道我们要更新在哪个 dom元素上
  • 接下来,判断传入的 oldvnode 和 vnode 是不是同一个节点,比较的就是 key 和sel,如果此时相同,就做更新 patchVnode(oldVnode,vnode)如果不相同,就会创建一个新的 DOM 元素,再插入这个新的 DOM 元素,最后移除老的 DOM 元素

根据新的虚拟 DOM,重新创建一个新的真实 DOM 节点,然后把旧的真实 DOM 替换掉。

2.2 patchVnode 函数

patchVnode 函数的过程

  • 首先就是给新的 vnode 挂上老的 vnode 对应的真实 dom 元素,也就是那个 elm,因为新的 vnode 很可能不知道之前要挂在哪个真实的 dom 节点上

  • 然后就是比较老的 vnode 和新的 vnode 的 children,如果相同,就直接返回

  • 接下来就是很重要的逻辑,children 不同的那些情况都怎么去判断实现

    • 新 vnode 无 text

      • 新 vnode有 children
        • 老的 vnode也有children,此时就要用 updateChildren 做更新
        • 老的 vnode 没有children,但是有 text,那么就将真实 dom 元素中的 text清空,将新的 vnode 的 children 添加到真实的 dom 上
      • 新 vnode没有 children
        • 这种情况应该是一个空节点,如果老的存在就对应的在真实 dom 上删掉即可
    • 新的 vnode 有 text,没有 children

      • 老的 vnode 存在 text,首先做比较,老的 vnode 的 text不等于新的vnode 的 text,此时如果老的 vnode 里面有 children,则删除真实 dom 元素的 children,设置真实的 dom 元素的 text 为新 vnode 的 text
      • 老的 vnode 不存在 text,那真实 dom 就更新成新 vnode,老的 vnode 有 children ,真实 DOM 里的 children 清掉,再设置 text

      注意:

      text 和children 根据 vnode 的定义,两者只会出现一个,是对立的,切记!

2.3 updateChildren函数

updateChildren函数的过程:

javascript 复制代码
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
  // 依次尝试 4 种命中
}

循环的主控条件是两边都没处理完

4 种命中分别是

  • 新头比旧头
  • 新头比旧尾
  • 新尾比旧头
  • 新尾比旧尾

如果都没命中,走兜底操作,比较 key 值

为什么要一轮一轮的进行四种命中呢?

因为这是一种很省成本的启发式策略,本质在赌一个事实,前端列表更新里,很多变动其实都发生在头尾附近。比如头部插入,尾部追加等等.优先处理最常见的场景

如果这 4 种方式命中了,命中后先 patchVnode,必要时做 DOM 移动,然后再移动对应指针。那么就是对应的指针++或者--

比如新头和旧尾命中了,那就是 newStartIdx++,oldEndIdx--

如果没命中,那就以新头指针为一个标志,拿着他的 key,去旧的列表中去找可复用的节点,如果没找到就创建新的,如果找到了但是tag 不同,那也创建新的,如果相同,也是继续去调用patchVnode 函数进行比较,

对应的指针 newStartIdx肯定是++,旧列表的对应元素,设置一个记号,被复用的位置标记为空位 / undefined,表示这个位置已经处理过了,后续跳过(在匹配上的情况)

相关推荐
kaixiang3002 小时前
若依RuoYi实战
java·服务器·前端
NocoBase2 小时前
为 Excel 数据快速构建 Web 应用:4 种方法对比
前端·人工智能·低代码·开源·excel
一 乐2 小时前
智能农田管理|基于springboot + vue智能农田管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·智能农田管理系统
苏瞳儿2 小时前
数据库的增删改查-node.js
前端·javascript·数据库
阿正的梦工坊2 小时前
pnpm和npm前端包管理工具有什么不同?
前端·npm·node.js
吴声子夜歌2 小时前
Node.js——Web模板引擎
前端·node.js
skywalk81632 小时前
Kotti Next的tinyfrontend前端模仿Kotti 首页布局(使用CodeArts)
前端
坐吃山猪2 小时前
TypeScript编程03-枚举
前端·javascript·typescript
冰暮流星2 小时前
javascript之dom查询操作2
开发语言·javascript·ecmascript