Ⅰ vue3 在哪些方便做了性能提升
概要
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。vue2版本阶段已经证明了它的易用性和流行性,说明它已经足够优秀在构建前端应用领域,而vue3的推出更是将性能提升做了最大的优化,更加易用、灵活、高效,未来是属于vue3的时代,因此深入了解vue3相对vue2在哪些方面做了性能提升,怎么提升性能的是非常有必要的。
编译阶优化段
在Vue2中,每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把用到的数据property记录为依赖,当依赖发生改变,触发setter,则会通知watcher,从而使关联的组件重新渲染
假如一个vue组件有如下模板结构:
可以看到,组件内部只有一个动态节点,剩余一堆都是静态节点,所以这里很多 diff 和遍历其实都是不需要的,会造成性能浪费。因此,Vue3在编译阶段,做了进一步优化。主要有如下:
diff算法优化
vue3在diff算法中相比vue2增加了静态标记关于这个静态标记,其作用是为了会发生变化的地方添加一个flag标记,下次发生变化的时候直接找该地方进行比较,如下图
静态类型如下所示
静态提升
Vue3中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用这样就免去了重复的创建节点,大型应用会受益于这个改动,免去了重复的创建操作,优化了运行时候的内存占用
没有做静态提升之前
做了静态提升之后
静态内容_hoisted_1被放置在render 函数外,每次渲染的时候只要取 _hoisted_1 即可同时 _hoisted_1 被打上了 PatchFlag ,静态标记值为 -1 ,特殊标志是负整数表示永远不会用于 Diff
预字符串化
在平时vue开发过程中,组件当中没有特别多的动态元素,大多都是静态元素。比如:
在这个组件中,除了span元素是动态元素之外,其余都是静态节点,一般可以说是动静比,动态内容 / 静态内容,比例越小,静态内容越多,比例越大,动态内容越多,vue3的编译器它会非常智能地发现这一点,当编译器遇到大量连续的静态内容,会直接将它编译为一个普通字符串节点,因为它知道这些内容永远不会变化,都是静态节点。
然而在vue2中,每个元素都会变成虚拟节点,一大堆的虚拟节点😱,这些全都是静态节点,在vue3中它会智能地发现这一点。如下图所示,我们可以很明显的感受到vue3的巨大性能提升
缓存事件处理函数
比如存在如下事件处理函数
<button @click="count++">plus</button>
对比vue2和vue3的处理方式
在vue2中创建一个虚拟节点button,属性里面多了一个事件onclick,内容就是count++。在vue3中会认为这里的事件处理是不会变化的,不是说这次渲染是事件函数,下次就变成别的,于是vue3会智能地发现这一点,会做缓存处理,它首先会看一看缓存里面有没有这个事件函数,有的话直接返回,没有的话就直接赋值为一个count++函数,保证事件处理函数只生成一次。
SSR优化
当静态内容大到一定量级时候,会用createStaticVNode方法在客户端去生成一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。
编译前
编译后
源码体积有优化
与Vue2相比较,Vue3整体体积变小了,移除了一些比较冷门的feature:如 keyCode 支持作为 v-on 的修饰符、on、off 和 $once 实例方法、filter过滤、内联模板等。tree-shaking 依赖 ES2015 模块语法的静态结构(即 import 和 export),通过编译阶段的静态分析,找到没有引入的模块并打上标记。任何一个函数,如ref、reavtived、computed等,仅仅在用到的时候才打包,没用到的模块都被摇掉,打包的整体体积变小。
响应式实现优化
改用proxy api做数据劫持
响应式是惰性的
http://vue-next-template-explorer.netlify.app/
Ⅱ vue-diff算法
渲染真实DOM的开销是很大的,轻微的操作都可能导致页面重新排版,非常耗性能。 相对于DOM对象,js对象处理起来更快,而且更简单。 通过diff算法对比新旧vdom之间的差异,可以批量的、最小化的执行 dom操作,从而提高性能。
常规:O(n^3) 遍历树1; 遍历树2; 排序; 1000个节点,十亿的量级。
vue diff:O(n) 只比较同一层级 ;tag不相同,直接删掉重建; 通过key来标识区分相同节点。
旧节点:A、B、C、D
新节点:A、E、B、C、D
1.1 两个节点key是否相同(两个key都没有,即 undefined === undefined)
1.2 两个节点tag标签名是否一样
1.3 两个节点是否都为注释节点
1.4 两个节点的data isDef是否都相等(isDef:data !== undefined && data !== null)
1.5 两个节点的input类型是否相同
1.6 节点a是否为异步占位
1.7 两个节点的异步函数是否相等
1.8节点b异步函数的error是否为空(isUndef:data === undefined && data === null)
2.1 新旧节点都有子节点,调用updateChildren重排子节点
2.2 只有新节点有子节点,调用addVnodes添加子节点
2.3 只有旧节点有子节点,调用removeVnodes移除子节点
2.4 如果是文本节点,调用setTextContent更新节点文本内容
3.1.1 旧头不存在,将旧头游标往后移一位
3.1.2 旧尾不存在,将旧尾游标往前移一位
3.1.3 旧头、新头相同,更新节点,并将头部游标往后移一位
3.1.4 旧尾、新尾相同,更新节点,并将尾部游标往前移一位
3.1.5 旧头、新尾相同,更新节点,并且将旧头移到尾部,旧头游标往后移一位,新尾游标往前移一位
3.1.6 旧尾、新头相同,更新节点,并且将旧尾移到头部,新头游标往后移一位,旧尾游标往前移一位
3.1.7 拿新头遍历旧子节点,找不到则新建一个节点;找到判断节点是否相同,相同则更新节点,移动老节点,不同则新建一个节点
3.2.1 旧子节点先遍历完毕,说明有新增节点,批量增加
3.2.2 新子节点先遍历完毕,说明有节点删除,批量移除
Ⅲ 原生快还是vue3快
vue3快。
_略隽巳鲎榧_ragment 支持多个根节点、Suspense 可以在组件渲染之前的等待时间显示指定内容、Teleport 可以让子组件能够在视觉上跳出父组件(如父组件overflow:hidden)
_略鲋噶? v-memo,可以缓存 html 模板,比如 v-for 列表不会变化的就缓存,简单说就是用内存换时间
_С? Tree-Shaking,会在打包时去除一些无用代码,没有用到的模块,使得代码打包体积更小
_略? Composition API 可以更好的逻辑复用和代码组织,同一功能的代码不至于像以前一样太分散,虽然 Vue2 中可以用 minxin 来实现复用代码,但也存在问题,比如方法或属性名会冲突,代码来源也不清楚等
_? Proxy 代替 Object.defineProperty 重构了响应式系统,可以监听到数组下标变化,及对象新增属性,因为监听的不是对象属性,而是对象本身,还可拦截 apply、has 等13种方法
_毓沽诵槟? DOM,在编译时会将事件缓存、将 slot 编译为 lazy 函数、保存静态节点直接复用(静态提升)、以及添加静态标记、Diff 算法使用 最长递增子序列 优化了对比流程,使得虚拟 DOM 生成速度提升 200%
_С衷? 里使用 v-bind,给 CSS 绑定 JS 变量(color: v-bind(str))
_? setup 代替了 beforeCreate 和 created 这两个生命周期
_略隽丝⒒肪车牧礁龉匙雍谧榧率? onRenderTracked 会跟踪组件里所有变量和方法的变化、每次触发渲染时 onRenderTriggered 会返回发生变化的新旧值,可以让我们进行有针对性调试
_暇? Vue3 是用 TS 写的,所以对 TS 的支持度更好
Ⅳ Vue2.x和Vue3.x渲染器的diff算法
简单来说,diff算法有以下过程
正常Diff两个树的时间复杂度是 O(n^3) ,但实际情况下我们很少会进行 跨层级的移动DOM ,所以Vue将Diff进行了优化,从 O(n^3) -> O(n) ,只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。
Vue2的核心Diff算法采用了 双端比较 的算法,同时从新旧children的两端开始进行比较,借助key值找到可复用的节点,再进行相关操作。相比React的Diff算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅。
Vue3.x借鉴了 ivi 算法和 inferno 算法
在创建VNode时就确定其类型,以及在 mount/patch 的过程中采用 位运算 来判断一个VNode的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升。(实际的实现可以结合Vue3.x源码看。)
该算法中还运用了 动态规划 的思想求解最长递归子序列。
Ⅳ vue3加载性能
加载性能大大提升。
1、diff算法加入了静态标签的概念,是vue3加载性能的一大优势。
2、vue3内部使用了Tree-shaking技术,,没有使用到的api,使vue3加载性能大大提升。
3、vue3打包时更好的支持tree-shaking,因此整体体积更小,加载更快
Ⅵ web前端diff 算法深入一下
有同学问:能否详细说一下 diff 算法。
详细的说,请阅读这篇文章,有疑问的地方欢迎留言一起讨论。
因为 diff 算法是 vue2.x , vue3.x 以及 react 中关键核心点,理解 diff 算法,更有助于理解各个框架本质。
说到“diff 算法”,不得不说“虚拟 Dom”,因为这两个息息相关。
比如:
等等
我们先来说说虚拟 Dom,就是通过 JS 模拟实现 DOM ,接下来难点就是如何判断旧对象和新对象之间的差异。
Dom 是多叉树结构,如果需要完整的对比两棵树的差异,那么算法的时间复杂度 O(n ^ 3),这个复杂度很难让人接收,尤其在 n 很大的情况下,于是 React 团队优化了算法,实现了 O(n) 的复杂度来对比差异。
实现 O(n) 复杂度的关键就是只对比同层的节点,而不是跨层对比,这也是考虑到在实际业务中很少会去跨层的移动 DOM 元素。
虚拟 DOM 差异算法的步骤分为 2 步:
实际 diff 算法比较中,节点比较主要有 5 种规则的比较
部分源码 https://github.com/vuejs/vue/blob//src/core/vdom/patch.js#L501 如下:
在 reconcileChildren 函数的入参中
diff 的两个主体是:oldFiber(current.child)和 newChildren(nextChildren,新的 ReactElement),它们是两个不一样的数据结构。
部分源码
很多时候手工优化 dom 确实会比 virtual dom 效率高,对于比较简单的 dom 结构用手工优化没有问题,但当页面结构很庞大,结构很复杂时,手工优化会花去大量时间,而且可维护性也不高,不能保证每个人都有手工优化的能力。至此,virtual dom 的解决方案应运而生。
virtual dom 是“解决过多的操作 dom 影响性能”的一种解决方案。
virtual dom 很多时候都不是最优的操作,但它具有普适性,在效率、可维护性之间达到平衡。
virutal dom 的意义:
vue2.x 的 diff 位于 patch.js 文件中,该算法来源于 snabbdom,复杂度为 O(n)。了解 diff 过程可以让我们更高效的使用框架。react 的 diff 其实和 vue 的 diff 大同小异。
最大特点:比较只会在同层级进行, 不会跨层级比较。
对比之前和之后:可能期望将 直接移动到
的后边,这是最优的操作。
但是实际的 diff 操作是:
vue 中也使用 diff 算法,有必要了解一下 Vue 是如何工作的。通过这个问题,我们可以很好的掌握,diff 算法在整个编译过程中,哪个环节,做了哪些操作,然后使用 diff 算法后输出什么?
解释:
mount 函数主要是获取 template,然后进入 compileToFunctions 函数。
compileToFunction 函数主要是将 template 编译成 render 函数。首先读取缓存,没有缓存就调用 compile 方法拿到 render 函数的字符串形式,在通过 new Function 的方式生成 render 函数。
compile 函数将 template 编译成 render 函数的字符串形式。后面我们主要讲解 render
完成 render 方法生成后,会进入到 mount 进行 DOM 更新。该方法核心逻辑如下:
上面提到的 compile 就是将 template 编译成 render 函数的字符串形式。核心代码如下:
compile 这个函数主要有三个步骤组成:
分别输出一个包含
parse 函数:主要功能是 将 template 字符串解析成 AST(抽象语法树) 。前面定义的 ASTElement 的数据结构,parse 函数就是将 template 里的结构(指令,属性,标签) 转换为 AST 形式存进 ASTElement 中,最后解析生成 AST。
optimize 函数(src/compiler/optomizer.js):主要功能是 标记静态节点 。后面 patch 过程中对比新旧 VNode 树形结构做优化。被标记为 static 的节点在后面的 diff 算法中会被直接忽略,不做详细比较。
generate 函数(src/compiler/codegen/index.js):主要功能 根据 AST 结构拼接生成 render 函数的字符串 。
其中 genElement 函数(src/compiler/codgen/index.js)是根据 AST 的属性调用不同的方法生成字符串返回。
总之:
就是 compile 函数中三个核心步骤介绍,
patch 函数 就是新旧 VNode 对比的 diff 函数,主要是为了优化 dom,通过算法使操作 dom 的行为降低到最低, diff 算法来源于 snabbdom,是 VDOM 思想的核心。snabbdom 的算法是为了 DOM 操作跨级增删节点较少的这一目标进行优化, 它只会在同层级进行,不会跨层级比较。
总的来说:
在创建 VNode 就确定类型,以及在 mount/patch 的过程中采用位运算来判断一个 VNode 的类型,在这个优化的基础上再配合 Diff 算法,性能得到提升。
可以看一下 vue3.x 的源码:https://github.com/vuejs/vue/blob//src/core/vdom/patch.js
对 oldFiber 和新的 ReactElement 节点的比对,将会生成新的 fiber 节点,同时标记上 effectTag,这些 fiber 会被连到 workInProgress 树中,作为新的 WIP 节点。树的结构因此被一点点地确定,而新的 workInProgress 节点也基本定型。在 diff 过后,workInProgress 节点的 beginWork 节点就完成了,接下来会进入 completeWork 阶段。
snabbdom 算法:https://github.com/snabbdom/snabbdom
定位:一个专注于简单性、模块化、强大功能和性能的虚拟 DOM 库。
snabbdom 中定义 Vnode 的类型(https://github.com/snabbdom/snabbdom/blob//src/vnode.ts#L12)
init 函数的地址:
https://github.com/snabbdom/snabbdom/blob//src/init.ts#L63
init() 函数接收一个模块数组 moles 和可选的 domApi 对象作为参数,返回一个函数,即 patch() 函数。
domApi 对象的接口包含了很多 DOM 操作的方法。
源码:
https://github.com/snabbdom/snabbdom/blob//src/init.ts#L367
源码:
https://github.com/snabbdom/snabbdom/blob//src/h.ts#L33
h() 函数接收多种参数,其中必须有一个 sel 参数,作用是将节点内容挂载到该容器中,并返回一个新 VNode。
在 vue2.x 不是完全 snabbdom 算法,而是基于 vue 的场景进行了一些修改和优化,主要体现在判断 key 和 diff 部分。
1、在 snabbdom 中 通过 key 和 sel 就判断是否为同一节点,那么在 vue 中,增加了一些判断 在满足 key 相等的同时会判断,tag 名称是否一致,是否为注释节点,是否为异步节点,或者为 input 时候类型是否相同等。
https://github.com/vuejs/vue/blob//src/core/vdom/patch.js#L35
2、diff 差异,patchVnode 是对比模版变化的函数,可能会用到 diff 也可能直接更新。
https://github.com/vuejs/vue/blob//src/core/vdom/patch.js#L404
Ⅶ Vue3基础-模板语法
如果我们希望把数据显示到模板(template)中,使用最多的语法是 “Mustache”语法 (双大括号) 的文本插值。
并且我们前端提到过,data返回的对象是有添加到Vue的响应式系统 中,当data中的数据发生改变时,对应的内容也会发生更新。
当然,Mustache中不仅仅可以是data中的属性,也可以是一个JavaScript的表达式:
下面这种写法是语句不是表达式,所以是错误的:
v-once用于指定元素或者组件只渲染一次,当数据发生变化时,元素或者组件以及其所有的子元素将视为静态内容并且跳过,该指令可以用于性能优化。
如果添加到父节点,那么所有的子节点也是只会渲染一次:
用于更新元素的 textContent,等价于"Mustache"语法,而且"Mustache"语法更灵活。
默认情况下,如果我们展示的内容本身是 html 的,那么vue并不会对其进行特殊的解析。如果我们希望这个内容被Vue可以解析出来,那么可以使用 v-html 来展示。
效果如下:
v-pre用于跳过元素和它的子元素的编译过程,显示原始的Mustache标签。
跳过不需要编译的节点,加快编译的速度。
效果如下:
这个指令保持在元素上直到关联组件实例结束编译。
v-cloak 和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到组件实例准备完毕,主要用于解决闪动问题,现在Vue3一般不会出现这个问题了。
<div> 不会显示,直到编译结束。
前面讲的一系列指令,主要是将值插入到模板内容中。但是,除了内容需要动态来决定外,某些属性我们也希望动态来绑定。比如动态绑定a元素的href属性,动态绑定img元素的src属性。
绑定属性我们使用 v-bind: ,缩写 : ,用于动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。
v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值(这个学到组件时再介绍),在开发中,有哪些属性需要动态进行绑定呢?还是有很多的,比如图片的链接src、网站的链接href、动态绑定一些类、样式等等。
v-bind有一个对应的语法糖,也就是简写方式,在开发中,我们通常会使用语法糖的形式,因为更简洁。
注意 :Vue2 template模板中只能有一个根元素,Vue3 template模板中允许有多个根元素。
在开发中,有时候我们的元素class也是动态的,比如:当数据为某个状态时,字体显示红色,当数据另一个状态时,字体显示黑色。
绑定class有两种方式:对象语法,数组语法。
① 对象语法:我们可以传给 :class (v-bind:class 的简写) 一个对象,以动态地切换 class。
② 数组语法:我们可以把一个数组传给 :class,以应用一个 class 列表;
我们可以利用v-bind:style来绑定一些CSS内联样式,这是因为某些样式我们需要根据数据动态来决定,比如某段文字的颜色,大小等等。
CSS属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名。
绑定style有两种方式:对象语法,数组语法。
① 对象语法:
② 数组语法: :style 的数组语法可以将多个样式对象应用到同一个元素上
在某些情况下,我们属性的名称可能也不是固定的。
前面我们无论绑定src、href、class、style,属性名称都是固定的,如果属性名称不是固定的,我们可以使用 :[属性名]=“值” 的格式来定义,这种绑定的方式,我们称之为动态绑定属性。
如果我们希望将一个对象的所有属性,绑定到元素上的所有属性,应该怎么做呢?非常简单,我们可以直接使用 v-bind 绑定一个对象。
如下:info对象会被拆解成div的各个属性。
前面我们绑定了元素的内容和属性,在前端开发中另外一个非常重要的特性就是交互。
在前端开发中,我们需要经常和用户进行各种各样的交互,这个时候,我们就必须监听用户发生的事件,比如点击、拖拽、键盘事件等等。
在Vue中如何监听事件呢?使用v-on指令。接下来我们来看一下v-on的用法:
我们可以使用v-on来监听一下点击的事件:
v-on:click可以写成@click,是它的语法糖写法:
当然,我们也可以绑定其他的事件:
如果我们希望一个元素绑定多个事件,这个时候可以传入一个对象:
当通过methods中定义方法,以供@click调用时,需要注意参数问题:
情况一:如果该方法不需要额外参数,那么方法后的()可以不添加,并且方法的实现不用参数,直接就可以打印event。
情况二:如果需要同时传入某个参数和event时,可以通过$event传入事件,并且方法的实现必须按顺序写明参数。
@keyup.enter 代表enter键弹起的时候会调用onEnter方法,我们一般在方法里面获取输入的值:
在某些情况下,我们需要根据当前的条件决定某些元素或组件是否渲染,这个时候我们就需要进行条件判断了。
Vue提供了下面的指令来进行条件判断:
下面我们来对它们进行学习。
v-if、v-else、v-else-if 用于根据条件来渲染某一块的内容,这些内容只有在条件为true时,才会被渲染出来,这三个指令与JavaScript的条件语句 if、else、else if 类似。
v-if 的渲染原理:v-if是惰性的,当条件为false时,其判断的内容完全不会被渲染或者会被销毁掉,当条件为true时,才会真正渲染条件块中的内容。
因为v-if是一个指令,所以必须将其添加到一个元素上,但是如果我们希望切换的是多个元素呢?
如果此时我们使用div包裹,div会被渲染到界面上来,但是我们并不希望div被渲染,这个时候,我们可以选择使用template,template元素可以当做不可见的包裹元素,并且 v-if 可以添加到 template 上,但是最终template不会被渲染出来,类似于小程序中的block。
v-show和v-if的用法看起来是一致的,也是根据一个条件决定是否显示元素或者组件。
首先,在用法上的区别:
其次,本质的区别:
开发中如何进行选择呢?
在真实开发中,我们往往会从服务器拿到一组数据,并且需要对其进行渲染。这个时候我们可以使用v-for来完成,v-for类似于JavaScript的for循环,可以用于遍历一组数据。
v-for的基本格式是 "item in 数组" ,数组通常是来自data或者prop,也可以是其他方式,item是我们给每项元素起的一个别名,这个别名可以自定来定义。
我们知道,在遍历一个数组的时候会经常需要拿到数组的索引,如果我们需要索引,可以使用格式 "(item, index) in 数组" ,注意顺序,数组元素项item在前面,索引项index在后面。
类似于v-if,你可以使用 template 元素来循环渲染一段包含多个元素的内容。
我们使用template来对多个元素进行包裹,而不是使用div来完成,因为div会被渲染,template不会被渲染。而且如果有ul,ul里面不推荐放div,只推荐放li。
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新,这些被包裹过的方法包括:
上面的方法会直接修改原来的数组,所以视图会跟着更新。但是某些方法不会替换原来的数组,而是会生成新的数组,比如 filter()、concat() 和 slice(),这时候我们可以通过重新赋值的方式触发视图更新,如下:
在使用v-for进行列表渲染时,我们通常会给元素或者组件绑定一个key属性。
这个key属性有什么作用呢?
我们先来看一下官方的解释:key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes。如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法,而使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素。
官方的解释对于初学者来说并不好理解,比如下面的问题:
什么是新旧nodes,什么是VNode?
没有key的时候,如何尝试修改和复用的?
有key的时候,如何基于key重新排列的?
我们先来解释一下VNode的概念:
VNode的全称是Virtual Node,也就是虚拟节点。事实上,无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode。VNode的本质是一个JavaScript的对象。
如果我们不只是一个简单的div,而是有一大堆的元素,那么它们应该会形成一个VNode Tree。
我们先来看一个案例:这个案例是当我们点击按钮时会在li中间插入一个f。
我们可以确定的是,这次更新对于ul和button是不需要进行更新,需要更新的是我们li的列表。在Vue中,对于相同父元素的子元素节点并不会重新渲染整个列表,因为对于列表中 a、b、c、d它们都是没有变化的。在操作真实DOM的时候,我们只需要在中间插入一个f的li即可。
那么Vue中对于列表的更新究竟是如何操作的呢?
Vue事实上会对于有key和没有key会调用两个不同的方法,有key,那么就调用 patchKeyedChildren方法,没有key,那么就调用 patchUnkeyedChildren方法。
没有key的diff算法:
我们会发现上面的diff算法效率并不高,c和d来说它们事实上并不需要有任何的改动,但是因为我们的c被f所使用了,所有后续所有的内容都要一次进行改动,并且最后进行新增。
有key的diff算法:
所以我们可以发现,Vue在进行diff算法的时候,会尽量利用我们的key来进行优化操作,在没有key的时候我们的效率是非常低效的,在进行插入或者重置顺序的时候,保持相同的key可以让diff算法更加的高效。
Ⅷ 面试中的网红Vue源码解析之虚拟DOM,你知多少呢深入解读diff算法
众所周知,在前端的面试中,面试官非常爱考dom和diff算法。比如,可能会出现在以下场景
滴滴滴,面试官发来一个面试邀请。接受邀请📞
我们都知道, key 的作用在前端的面试是一道很普遍的题目,但是呢,很多时候我们都只浮于知识的表面,而没有去深挖其原理所在,这个时候我们的竞争力就在这被拉下了。所以呢,深入学习原理对于提升自身的核心竞争力是一个必不可少的过程。
在接下来的这篇文章中,我们将讲解面试中很爱考的虚拟DOM以及其背后的diff算法。 请认真阅读本文~文末有学习资源免费共享!!!
虚拟DOM是用JavaScript对象描述DOM的层次结构。DOM中的一切属性都在虚拟DOM中有对应的属性。本质上是JS 和 DOM 之间的一个映射缓存。
要点:虚拟 DOM 是 JS 对象;虚拟 DOM 是对真实 DOM 的描述。
diff发生在虚拟DOM上。diff算法是在新虚拟DOM和老虚拟DOM进行diff(精细化比对),实现最小量更新,最后反映到真正的DOM上。
我们前面知道diff算法发生在虚拟DOM上,而虚拟DOM是如何实现的呢?实际上虚拟DOM是有一个个虚拟节点组成。
h函数用来产生虚拟节点(vnode)。虚拟节点有如下的属性:
1)sel: 标签类型,例如 p、div;
2)data: 标签上的数据,例如 style、class、data-*;
3)children :子节点;
4) text: 文本内容;
5)elm:虚拟节点绑定的真实 DOM 节点;
通过h函数的嵌套,从而得到虚拟DOM树。
我们编写了一个低配版的h函数,必须传入3个参数,重载较弱。
形态1:h('div', {}, '文字')
形态2:h('div', {}, [])
形态3:h('div', {}, h())
首先定义vnode节点,实际上就是把传入的参数合成对象返回。
[图片上传失败...(image-7a9966-1624019394657)]
然后编写h函数,根据第三个参数的不同进行不同的响应。
当我们进行比较的过程中,我们采用的4种命中查找策略:
1)新前与旧前:命中则指针同时往后移动。
2)新后与旧后:命中则指针同时往前移动。
3)新后与旧前:命中则涉及节点移动,那么新后指向的节点,移到 旧后之后 。
4)新前与旧后:命中则涉及节点移动,那么新前指向的节点,移到 旧前之前 。
命中上述4种一种就不在命中判断了,如果没有命中,就需要循环来寻找,移动到旧前之前。直到while(新前<=新后&&旧前<=就后)不成立则完成。
如果是新节点先循环完毕,如果老节点中还有剩余节点(旧前和旧后指针中间的节点),说明他们是要被删除的节点。
如果是旧节点先循环完毕,说明新节点中有要插入的节点。
1.什么是Virtual DOM 和Snabbdom
2.手写底层源码h函数
3.感受Vue核心算法之diff算法
4.snabbdom之核心h函数的工作原理
1、零基础入门或者有一定基础的同学、大中院校学生
2、在职从事相关工作1-2年以及打算转行前端的朋友
3、对前端开发有兴趣人群
Ⅸ vue3优化不包括
vue3优化不包括支持IE11。根据查询相关公开资料信息显示,Vue3做出的优化有diff算法优化、静态提升、事件监听缓存、SSR优化。目的是让代码更易于开发和维护。vue3不再支持IE11,Vue在2.X版本支持IE11。
Ⅹ 你怎么理解vue中的diff算法
diff算法是虚拟 DOM 的必然产物,通过新旧虚拟 DOM 对比,将变化的地方更新在真实 DOM 上,另外也需要 diff 高效的执行对比过程,从而降低时间复杂度
vue2中为了降低 watcher 力度,每个组件只有一个 watcher 与之对应,只有引入 diff 才能精确的找到发生变化的地方
diff 之所以发生变化,是引入组件数据的变化调用了 set 方法,导致 watcher能够监控到数据的变化,导致其触发 updateComponent 中 pathVnode方法,在 pathVnode 函数中对对比上一次渲染结果和新渲染结果.两个节比较的过程就是发生diff的过程.整个更新过程叫做 path 过程,也叫打补丁的过程
diff 过程整体遵从深度优先,同层比较的策略;两个节点之间比较会根据它们是否拥有子节点或者文本节点做不同的操作.比较两组节点是算法的重点.首先假设头尾节点相同做 4 次比较尝试,如果这 4 中方式都没有找到,就按照通用方式遍历查找.查找结束在按照情况处理剩下的节点;借助 key,通常可以精确的找到相同的节点,因此整个 path 过程是非常高效的