「废话不多说,今天的主题是和大家分享一下Vue 虚拟Dom、Diff算法原理以及源码解析、以及Vue3.0的diff算法是如何进行优化的。」 算法原理 Vue3.0 diff算法优化 virtual dom 如果咱们不了解virtual dom 的话,要理解DIff算法是比较困难的。 分析diff算法 由上我们知道了,新的虚拟DOM和旧的虚拟DOm是通过diff算法进行比较之后更新的。 Vue2.x diff算法 Vue2.x diff算法原理 传统diff算法通过循环递归对节点进行依次对比效率低下,算法复杂度达到O(N^3),主要原因在于其追求完全比对和最小修改,而React、Vue 则是放弃了完全比对及最小修改,才实现从O(N^3) => O(N)。
React diff 算法 这篇是译文,翻译自Christopher Chedeau的React’s diff algorithm。 在这篇文章中将展示React的diff算法是如何来优化你的app性能的。 diff算法 在我们详细解释算法之前,有必要了解下React是如何工作的。 如你所想,这么高复杂度的算法是无法满足我们的需求的。React使用了一种更为简单且直观的算法使得算法复杂度优化至O(n)。 React只会逐层对比两颗随机树。这大大降低了diff算法的复杂度。 React的diff算法也考虑了这种情况,它仅仅会匹配相同class(此处不是指dom的className,而是组件的类别)的组件。 它实现了一个与W3C标准兼容的事件系统。这意味着你不会遇到IE8的那些事件绑定bug。所有的事件在各个浏览器上都是一致的。 让我来简单解释下它是怎么做的。首先在文档的根节点上绑定一个事件监听器。
想要搞明白 Vue3 的 DOM Diff 核心算法,我们要从一道 LeetCode 真题说起。 我们先来一起读读题: LeetCode 真题 300. Vue3 DOM Diff 核心算法 搞清楚了最长递增子序列这道算法题,我们再来看 Vue3 的 DOM Diff 核心算法就简单的多了。 好了,进入本文的正题,Vue3 DOM Diff 核心算法。 首先我们要搞清楚,核心算法的的位置。核心算法其实是当新旧 children 都是多个子节点的时候才会触发。 下面这段代码就是 Vue3 的 DOM Diff 核心算法,我加上了在源码中的路径,方便你查找。 以上就是 Vue3 DOM Diff 的核心算法部分啦,欢迎光临前端食堂,客官您慢走~
Git内置的Diff算法 Git 是目前主流的版本控制系统,它的代码比对能力由 Git 内部的比对(Diff)算法实现。 Git 内置有 4 种 Diff 算法,即 Myers,Minimal,Patience 和 Histogram。其中 Myers 是 Git 使用的默认比对算法。 我们可以在执行比对命令 git diff 时,通过参数 --diff-algorithm 指定比对算法。 下面,本文将选择 Git 的默认比对算法 Myers,为大家进行详细讲解。 Myers算法简介 Myers Diff Algorithm 是 Git 中的默认代码比对算法,由 Eugene W. Myers 在 1986 年发表。 diff; } } Myers算法示例 最后,用上面复刻出来的 Myers 算法,来复现一下对字符串 a、b 之间的差异比较。
想要搞明白 Vue3 的 DOM Diff 核心算法,我们要从一道 LeetCode 真题说起。 我们先来一起读读题: LeetCode 真题 300. Vue3 DOM Diff 核心算法 搞清楚了最长递增子序列这道算法题,我们再来看 Vue3 的 DOM Diff 核心算法就简单的多了。 好了,进入本文的正题,Vue3 DOM Diff 核心算法。 首先我们要搞清楚,核心算法的的位置。核心算法其实是当新旧 children 都是多个子节点的时候才会触发。 下面这段代码就是 Vue3 的 DOM Diff 核心算法,我加上了在源码中的路径,方便你查找。 这也是为什么不着急说它的原因,因为如果你看懂了上面的 LeetCode 题解,你就已经掌握了 Vue3 的 Dom Diff 核心算法的思想啦。
面对复杂的情况,即使最前沿的算法,复杂度也极高。 场景上面说到diff算法应对三种场景:节点更新、节点增删、节点移动,但一个fiber的子元素有可能是单节点,也有可能是多节点。所以依据这两类节点可以再细分为:单节点更新、单节点增删。 若前后的节点tag或者key不相同,Diff算法会认为新节点和旧节点毫无关系。以下例子中,key为b的新节点的className发生了变化,是节点更新。 实际上在平时的实践中,节点自身的更新是最多的,所以Diff算法会优先处理更新的节点。 Diff算法的复杂度也因为这个操作大幅降低。let newIdx = 0;for (; oldFiber !
diff 概述 diff命令在最简单的情况下,比较给定的两个文件的不同。 如果使用“-”代替“文件”参数,则要比较的内容将来自标准输入。 diff命令是以逐行的方式,比较文本文件的异同处。 :不检查空白行; -c:显示全部内容,并标出不同之处; -C<行数>或–context<行数>:与执行“-c-<行数>”指令相同; -d或——minimal:使用不同的演算法,以小的单位来做比较 ---- diff3 概述 diff3命令用于比较3个文件,将3个文件的不同的地方显示到标准输出。 语法 diff3(选项)(参数) 选项 -a:把所有的文件都当做文本文件按照行为单位进行比较,即给定的文件不是文本文件; -A:合并第2个文件和第3个文件之间的不同到第1个文件中,有冲突内容用括号括起来 实例 [root@entel1 Videos]# diff3 1.txt 2.txt 3.txt ==== 1:1c aac 2:1c aab 3:1c aad
算法概述 diff即对比,是一个广泛的概念,如linux diff、git diff 两个JS对象也可以做diff 两颗树做diff,如vdom diff 树diff 时间负责度是o(n^3) 第一,遍历tree1;第二,遍历tree2 第三,排序 1000个节点,要计算 1亿次,算法不可用 优化时间复杂度到o(n) 之比较同一级,不跨级比较 tag不相同,则直接删掉重建,不再深度比较 tag和key两者都相同,则认为是相同节点,不再深度比较 在这里插入图片描述 在这里插入图片描述 patchVnode() diff算法总结 patchVnode 逻辑 addVnodes、removeVnodes updateChildren(key的重要性) 不使用key VS 使用key 不使用key:如果节点顺序变了,节点更新时需要全部删掉, 使用key:可以算出哪个key是相同的,可以直接移动 vdom和diff 总结 updateChildren的过程不要深究,要知道大概过程 vdom核心概念: h(传入的什么) vnode(结构) patch(作用、参数) diff(过程、性能优化做了哪些改变
什么是虚拟DOM 讲Diff算法前,我先给大家讲一讲什么是虚拟DOM吧。这有利于后面大家对Diff算法的理解加深。 虚拟DOM是一个对象,一个什么样的对象呢? 虚拟DOM算法 = 虚拟DOM + Diff算法 什么是Diff算法 上面咱们说了虚拟DOM,也知道了只有`虚拟DOM + Diff算法才能真正的提高性能,那讲完虚拟DOM,我们再来讲讲Diff算法`吧 总结: Diff算法是一种对比算法 。 (可能较多的节点)排版与重绘 Diff算法的原理 Diff同层对比 新旧虚拟DOM对比的时候,Diff算法比较只会在同层级进行, 不会跨层级比较。 所以Diff算法是:广度优先算法。
本文以 Vue 原码中的 DOM diff 算法为例,介绍一下这个算法的实现原理。 虚拟 DOM 是用 JavaScript 模拟 DOM 结构,通过计算出最小的变更,操作 DOM 结构,更新视图。 而 Diff 算法是虚拟 DOM 最核心、最关键的部分,好的 Diff 算法可以正确、快速的更新 DOM。DOM diff 算法时间复杂度为 O(n)。 patchVnode 函数 接下来是 diff 算法中最为核心的一个函数:updateChildren。 DOM diff 算法有以下几个特点: 先同级比较,再比较子节点; 先判断一方有 children,一方没有 children 的情况; 比较都有 children 的情况; 递归比较子节点; updateChildren 接着又一轮循环,结果发现循环条件不能满足,diff 算法结束,DOM 更新完成。
前言 在日常面试中,Diff算法都是绕不过去的一道坎, 用最通俗的话,讲最难的知识点 一直是我写文章的宗旨,今天我就用通俗的方式来讲解一下Diff算法吧? 虚拟DOM算法 = 虚拟DOM + Diff算法 什么是Diff算法 上面咱们说了虚拟DOM,也知道了只有`虚拟DOM + Diff算法才能真正的提高性能,那讲完虚拟DOM,我们再来讲讲Diff算法`吧 总结: Diff算法是一种对比算法 。 (可能较多的节点)排版与重绘 Diff算法的原理 Diff同层对比 新旧虚拟DOM对比的时候,Diff算法比较只会在同层级进行, 不会跨层级比较。 所以Diff算法是:广度优先算法。
问题3: 标签中加入一个 <input>标签会重新渲染吗? DIff算法逐层对比。
react/vue中遍历的key有什么作用? 我们来实现个例子,点击添加按钮在列表中添加一个小王。 2.详细的说:
当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较。
比较规则如下:
a).
Vue源码之虚拟DOM和diff算法(二) 手写diff算法 个人练习结果仓库(持续更新):Vue源码解析 patch函数简要流程 新旧节点不是同一个虚拟节点(新节点内容是 text) 不做过多解释了 = document.getElementById('container') patch(container, myVnode1) // 上树 const myVnode2 = h('h3' section', {}, [ h('p', {}, '赤'), h('p', {}, '蓝'), h('p', {}, '紫') ]) patch(myVnode1, myVnode2) diff ,则直接在旧子节点中寻找相同key的元素,不存在的话,新增并将该元素追加到旧前指针之前,新前指针下移 删除 位置变换 增 + 删 + 位置变化 key一样,节点内容却不同的情况 详解Vue的Diff 算法(例6) 原理总结 新前旧前: 命中,新前指针、旧前指针下移,回到1,继续看有没有命中 未命中,继续向下尝试命中 新后旧后: 命中,新后指针、旧后指针上移,回到1,继续看有没有命中 未命中,继续向下尝试命中
## 算法的实现 1. 比较两棵dom树的差距 比较两棵DOM树的差异是 Virtual DOM 算法最核心的部分,这也是所谓的 Virtual DOM 的 diff 算法。 两个树的完全的 diff 算法是一个时间复杂度为 O(n^3) 的问题。但是在前端当中,你很少会跨越层级地移动DOM元素。所以 Virtual DOM 只会对同一个层级的元素进行对比: ! 算法的核心。 步骤2 同样是比较两颗树的差异,也就是diff算法 !
找出需要重新渲染的范围,就是 Diff 的过程。React 和 Vue 的 Diff 算法思路基本一致,只对同层节点进行比较,利用唯一标识符对节点进行区分。 知识点深入 1. Diff 算法 两棵树的比对和更新,涉及到树编辑距离(Tree Editing Distance)算法:将一棵树转化为另一棵树的最小操作成本。操作类型包括:删除、插入、修改。 时间复杂度为 O(n^3)。 为了降低时间复杂度,React 和 Vue 的思路是基于以下两个假设条件,缩减递归迭代规模,将 Diff 算法的时间复杂度降低为 O(n): 相同类型的组件产生相同的 DOM 结构,反之亦然。 Vue2.x Diff Vue 的 Diff 算法和 React 的类似,只在同一层次进行比较,不进行跨层比较。如果两个元素被判定为不相同,则不继续递归比较。
1.传统的 Diff 算法 传统的递归算法通过循环递归遍历节点进行对比,其复杂度要达到O(n3),大体实现代码如下: const diffLeafs = function (beforeLeaf, afterLeaf 基于以上前提策略,React 分别对 Tree Diff、Component Diff、Element Diff 进⾏算法优化, 保证整体界⾯构建的性能。 3. 当 DOM 节点进⾏跨层级操作时,Diff 算法会如何处理? updateDepth 来标识递归的深度,进⾏ Diff 算法之前 +1,Diff 算法结束之后 -1。 当其重新变为 0 的时候表示整个 Diff 算法结束了,可以拿更新队列 diffQueue 来更新 DOM 了 Diff 算法只对同个⽗元素的同级⼦元素进⾏对⽐。
三、实现 diff 算法 这里我们做的就是实现一个 diff 算法进行虚拟节点 Element 的对比,并返回一个 patch对象,用来存储两个节点不同的地方。 而 diff 算法又包含了两个不一样的算法,一个是 O(n),一个则是 O(max(m, n)) 1、同层级元素比较(O(n)) 首先,我们的知道的是,如果元素之间进行完全的一个比较,即新旧 Element 算法流程如图所示 ? = 2 // text => 2 const REORDER = 3 // reorder => 3 // diff 入口,比较新旧两棵树的差异 function diff (oldTree 2、listDiff实现 O(m*n) => O(max(m, n)) 首先我们得明确一下为什么需要 list diff 这种算法的存在,list diff 做的一件事情是怎样的,然后它又是如何做到这么一件事情的
,这也是所谓的 Virtual DOM 的 diff 算法。 两个树的完全的 diff 算法是一个时间复杂度为 O(n^3) 的问题。但是在前端当中,你很少会跨越层级地移动DOM元素。 算法的核心。 ,初始数据为 1, 3 , 5 三个 1 3 5 三秒之后 我们变更为 1 2 比较 a,b两个节点 。 :h,diff,patch。
于是仔细研究并覆写了一遍针对数组变化的diff算法,在这里做下diff算法的逻辑分享&&源码解读 一.介绍前的准备工作 我们先了解diff方法的运行规则和前提方法. 1.虚拟node进行深度优先 && 3.构建索引 ? 逻辑如图: ? 二.如何处理元素 尽量不新增/删除dom,如图下所示: ? 如果是相同vnode,源码如下: ? 1-2.索引比较 -- 最坏情况,这里的时间复杂度也是O(n),即整个算法复杂度O(n)+O(n) 每次遍历的过程中可能存在"新数组节点新增/旧数组节点删除",那么前后对比就满足不了条件。 这里注意一个点,我们每次的节点更新会移动序号,即使被删除的节点不在一块 最终也会被 首尾比较算法 "摞在一块" 即 (oldStartIdx~oldEndIdx)。上图所示更加明显一些。 这样,整个diff的对比算法就已经走完了。所以核心就是:前后对比+索引 四.关键点 关键点大概如下 ?
Myer差分算法 举一个最常见的例子,我们使用 git 进行提交时,通常会查看这次提交做了哪些改动,这里我们先简单定义一下什么是 diff :diff 就是目标文本和源文本之间的区别,也就是将源文本变成目标文本所需要的操作 Myers算法 由 Eugene W.Myers 在 1986 年发表在 《 Algorithmica》 杂志上。的一篇论文中提出,是一个能在大部分情况产生最短的直观的diff 的一个算法。 [在这里插入图片描述] 寻找最短的直观的diff 是一个非常模糊的问题,首先,我们需要把这个问题抽象为一个具体的数学问题,然后再来寻找算法解决。 (diff算法将两个文件作为输入。第一个通常是较旧的文件是文件A,第二个是文件B。算法生成指令以将文件A转换为文件B。) 算法实践:DiffUtil和它的差量算法 参考链接: 代码:diff-match-patch diff2论文 Myers diff alogrithm:part 1 Myers diff alogrithm