虚拟dom-Diff算法

虚拟dom-Diff算法

vue2

diff算法在vue2中就是patch,通过新旧虚拟dom对比,找到最小变化然后进行dom操作

在页面首次渲染的时候会调用一次patch并创建新的vnode,不会进行深层次的比较,然后再组件中数据发生变化的时候,会触发setter然后通过notify通知watcher,对应的watcher会通知更新并执行更新函数,会执行render函数获取新的虚拟dom,然后执行patch对比上次渲染结果的老的dom,计算出最小的变化,根据最小的变化去更新真实的dom

什么是diff算法

diff是新旧内容之间的区别经计算,vue中的diff算法,就是通过一种简单而且高效的手段对比出新旧节点数组之间的区别以便以最小的dom操作来更新页面内容

  • 对比的是vnode数组
  • 同时存在新旧两组vnode数组
    真实的dom节点都是树的形式存在的,根节点都是, 为了保证虚拟节点能和真实dom节点一样,vnode也采用树形结构
    如果再组件更新的时候,需要对比全部vnode节点的话,新旧两组节点需要及进行深度遍历和比较,会产生很大的性能开销,所以,vue中默认同层节点比较,多余层级的内容会直接新建或者舍弃,只再同层级进行diff操作
    一般来说,diff操作一般发生在v-for循环或者有v-if/v-else,component这类动态生成的节点对象上

diff算法的优化

只会对比同一层级,不会跨级比较
比较标签名

如果同一层级的比较标签名不同,旧直接移除老的虚拟节点

比较key

从标签名相同,key相同,就会被认为是相同节点,也不继续按照这个树状结果做深度比较,比如写v-for的时候会比较key,不写key就会报错

在不使用key或者列表的index作为key的时候,每个元素对应的位置关系都是index,如果没有key的话,后续的li都会重新渲染。如果使用key的话,后续会判断li3和li4没有发生变化,所以不会重新渲染。

  • key的作用是为了更加搞笑的更新虚拟dom,因为可以非常精确的找到相同节点
  • vue在patch过程中会判断两个节点是不是相同节点,key是一个必要条件。如果在渲染列表的时候,不写key,vue在比较的时候,导致频繁更新元素,使得整个patch比较低效
  • 避免使用数组下标作为key,因为key值不是唯一的话,可能导致出现上图的bug。
  • vue判断两个节点是否相同的时候需要主动判断两者的元素类型和key,如果不设置key,就可能被认为两个相同的节点,只能做更新操作,造成大量不必要的dom更新操作。

双端diff

简单diff算法: 通过暴力手段直接遍历两个数组。双端就是从两端开始分别从中间进行遍历对比的算法

  1. 新旧头相等
  2. 新旧尾相等
  3. 旧头等于新尾
  4. 旧尾等于新头
  5. 四者互不相等
新头旧尾
头头相等
尾尾比较
新尾旧头
循环比较

增删

循环没有找到

头头,尾尾,头尾, 尾头没有匹配到,然后循环遍历,如果循环遍历没有找到,则生成这个节点。

children内增加节点
头头
尾尾
children内减少节点
增加整个children
删除整个children

vue3

什么时候用到了diff算法

存在children的vnode类型,element类型中vnode中存在children

flagment碎片类型vnode,可以创建一个具有多个dom节点的组件的方法就是创建一个没有底层vue实例的功能组件

javascript 复制代码
<template>
    <span>1</span>
    <span>2</span>
    <span>3</span>
</template>
// flagment出现就是看起来像一个普通的dom元素,但是是虚拟的,不会在dom树中呈现
<Fragment>
    <span>1</span>
    <span>2</span>
    <span>3</span>
</Fragment>

patchChildren根据是否存在key进行真正的diff或者直接patch

diff算法

头和头比
尾和尾比
基于最长递增子序列进行移动/添加/删除

先进行头和头比较,发现不同旧结束循环

然后进行尾和尾比较,发现不同就结束循环

在保存没有比较过的节点,并通过newIndexToOldIndexMap拿到数组里面对应的下标,生成数组,-1是老数组里没有的就是说明是新增

然后取出数组里面的最长递增子序列,

然后只需要把其他的剩余节点,基于最长子序列的位置进行移动新增和删除就可以了

比较新老children的length获取最小值,然后对于公共部分,重新进行patch工作

如果老节点数量大于新的节点数量,移除多出来的节点

如果新的节点数量大于老的节点数量,重新mountChildren新增的节点

如果老节点是否全部patch,新节点没有被patch完,创建新的vnode

如果新节点全部被patch,老节点有剩余,那么卸载所有老节点

最长子序列

这个核心,后续回写

相关推荐
半桶水专家2 分钟前
Vue 3 动态组件详解
前端·javascript·vue.js
csj507 分钟前
前端基础之《React(6)—webpack简介-图片模块处理》
前端·react
我有一棵树7 分钟前
避免 JS 报错阻塞 Vue 组件渲染:以 window.jsbridge 和 el-tooltip 为例
前端·javascript·vue.js
Fanfffff7208 分钟前
前端样式局部作用域:从Scoped到CSS Modules 的完整指南
前端·css
前端大神之路9 分钟前
vue2 模版编译原理
前端
00后程序员张9 分钟前
Web 前端工具全流程指南 从开发到调试的完整生态体系
android·前端·ios·小程序·uni-app·iphone·webview
凌泽33 分钟前
写了那么多年的代码,我开始写“规范”了:AI 驱动的开发范式革命
前端·vibecoding
没有鸡汤吃不下饭35 分钟前
解决前端项目中大数据复杂列表场景的完美方案
前端·javascript·vue.js
ゞ 正在缓冲99%…1 小时前
leetcode1312.让字符串成为回文串的最少插入次数
数据结构·算法·leetcode·动态规划·记忆化搜索
旧雨散尘1 小时前
【react】react初学6-第一个react应用-待办事项
前端·react.js·前端框架