编译优化
是什么?
编译优化 指的是编译器将 模板(template) 编译为 渲染函数(render) 的过程中,尽可能的 提取关键信息,以达到 生成最优代码 的过程。
为什么需要?
传统的 Diff 算法会存在很多无意义的对比操作
在对比 新旧 两颗 虚拟 DOM 时,总是要按照 虚拟 DOM 的 层级结构 “一层一层” 进行遍历,然后其中某些变量之间的关系内容的遍历对比是完全没必要的,例如:
<div id="foo">
<p class="bar">{{ text }}</p>
</div>
其中唯一可能变化的就是 <p>尤雨溪个人资料;
标签中的 text
值,当响应式数据 text
发生修改时,最高效的更新方式就是直接更新 <p>
标签对应的文本内容,然而对于 传统 Diff 算法 而言,会先根据 render
函数生成 新的虚拟 DOM,然后变量与函数再对比 新旧虚拟 DOM 的方式:
- 对比
div
节点,以及该节点的属性和子节点 - 对比
p
节点,以及该节点的属性和子节点 - 对比
p
节点的文本节点,发现文本节点发生变化,于是更新文本节点
编译思路
Vue.js3 编译优化的思路来源就是,跳过这些无意义的对操作,进一步的提升 Vue 中 Diff 算法的对比性能:
- 模板的结构相对稳定,在编译阶段尽可能提取关键信息(如:标记静态节点、动态节点)
- 基于关键信息,通过编译初始化sdk什么意思器直接生成对应的尤雨溪原生 DOM 操作代码,减少生成 虚拟 DOM 的性能消耗,有利于提升初始化渲染的速度
实验性的新编译策略
从理论上来看,某些情况初始化电脑时出现问题下确实并不需要 虚拟 DOM,(即 No Virtual DOM),但在 Vue.js3 中仍然选择保留虚拟 DOM,并承受其带来的性能开销,主要是考虑到 渲染函数的灵活性 和 V初始化是什么意思ue.js2html文件怎么打开 的兼容性 问题.
感兴趣可以去了解下,未来 Vue 会提供的一些新特性,不过这并不是本文的核心内容,State of Vue 2022-尤雨溪
Vue3 中的编译优化的方式
标记动态节点
标记动态节点之后,在后续渲染器更初始化电脑时出现问题新阶段旧可以直接基于动态节点集合,实变量与函数现对动态节点的 靶向更新 或 定向更新.
patchFlag 属性
在编译器进行编译时,如果判断当前节点是属于 动态节点,就会为这个 vnode尤雨溪
节点打上 patchFlag 标记
,也就是添加一个 patchFlag 属性缓存视频在手机哪里找
,并且 patchFlag 属性
对应的 数值 代表了当前这个 动态节点的类型,如:
- 数字
1
:代表该节点是 动html个人网页完整代码态 的textContent
- 数字
2
:代表该节点是 动态 的calss 绑定
- 数字
3
:缓存清理代表该节点是 动态 的style 绑定
- …
dynamicChildren 属性
dynamicChildren
属性 值对应的是一个数组,其中保存的就是带有 patchFlag
属性 的 vnode
节点,并且带有 dynamicChildren
属性 的 vnode
节点成称为 块,即 Block.
Block 节点
一个 Bloc缓存视频在手机哪里找k 本质上也是一个变量是什么意思 虚拟 DOM
节点,只不过它比普通的虚拟节点多了一个用于 存储动态子节点 的 dynamicChildren
属性.
一个 Block 不仅初始化电脑时出现问题能够收集它的 直接动态子节点html标签
,也能收集所有 动态的子代节点
,而后续渲染器的更新操作将以 Block 作为更新维度去处理.
什么样的节点会变成 Block 节点?
- 所有变量名模板缓存视频合并app的 根节点
- 带有
v-if
指令的节点 - 带有
v-for
指令的节点 - 模板中
Frament
节点所包裹的 多根节点
其中 v-if
和 v-for
指令会导致 更新前变量泵后模板结构不稳定,不过由于 v-for
指令渲染的是一组子节尤雨溪点,为了更好的表示这一组子节点,就需要使用 Fragment 节点来表达 v-for
指令的渲染结果,并将其作为 Block 节点.变量类型有哪些
静态提升
静态提升的目的是尽可能减少更新时创建 虚拟 DOM 带来的 性能开销 和 内初始化电脑时出现问题未进行更改存占用.
没有静态提升时带来的问题
通常,在响应式数据发生变化时,渲染函数就会重新执行,并产生新的虚拟 DOM 节初始化点,显然纯静态的虚拟节点缓存是什么意思完全没有必要重新创建,这会带来一定的性能开销.
解决方案
在编译阶段可以 将纯静态节点提升到渲染函数外部,在渲染函数内部保持对静态节点的引用即可,当响应尤雨溪式数据变化引起渲染函数重新执行时,并不会重新创建静态的虚拟节点,这样旧可以避免重复创建静态节点的虚拟 DOM 带来的性能开销.
值得注意的是,静态提html简单网页代码升是以树为单位的,毕竟不可能会为每一个小的静态节点进行静态提升,这会导致渲染函数外部变量与函数对应存储静态节点的变量增多,这也会 占用一定的内存.
预字符串化
基于 静态提升 可以继续采用 预字符串化 的优化手段,即直变量的定义接将原本需要以树为单位进行静态提升的内容,直接转换为对应基于 DOM 操作的 字符串形式.
预字符串化的优势如下:
- 大块的静态内容可以直接通过
innerHTML
进行设置,在 初始化渲染 时具有一定的性能优势 - 减少创建虚拟节点产生开销的性能
- 减少内存占用
缓存内联事件处理函数
不缓存内联事件函数带来的问题
在模HTML板事件处理函数中,为了一些简单的更新操作,通常会在模板中编写 内联的事件处理函数,例如:
<Comp @change="c = a + b"> ===> function render(ctx){
return h(Com, {
// 内联事件处理函数
onChange: () => ctx.c = ctx.a + ctx.b
})
}
显然,当 render
函数被重新执行时,都为会 Comp
组件创建一个全新的 props
对象,并且其中的 onChange
事件也是一个全新的函数,这会导致渲染器对 Comp
组件进行更新,造成额外的性能开销。
解决方案
通过为 render
渲染函数传递第二个参数 cache
数缓存英文组,且这个 cache
数组是缓存英文来自于组件实例的,因此可以将内联事件处理函数添加到 cache
数组中缓存起来.
当渲染函数html简单网页代码重新执行时并创建虚拟 DOM
时,优先从缓存中读取对应的事件处理函数,避免事件处理函数被重新创建,导致 Comp
组件进行无变量的定义用更新.
v-once 缓存虚拟 DOM
Vue.js2 和 Vue.js3 中都支持 v-o尤雨溪个人资料nce
指令,当前编译器遇到 v-once
指令时,会利用上面提到的 cache
数组来缓存渲染函数的全变量是什么意思部或部分执行结果.
v-once 的优势
- 避免组件更新时重新创建虚拟 DOM 带来的性能开销,因为虚拟 DOM 被缓存了,因此更新时无需重新创建
- 避免无用的 Diff 开销变量类型有哪些,这是因为被
v-once
标记的虚拟 DOM 树会被父级 Block 节点收集