关于Vue3编译器的一些优化

简介: Vue3 编译器本章主要介绍Vue3编译器的作用,这个编译器是如何提高性能的,静态dom与动态dom的不同处理,缓存的使用以及块的作用。致谢Vue Mastery非常好的课程,可以转载,但请声明源链接:文章源链接justin3go.com(有些latex公式某些平台不能渲染可查看这个网站)
published: true
date: 2022-2-3
tags: '前端框架 Vue'


Vue3 编译器

本章主要介绍Vue3编译器的作用,这个编译器是如何提高性能的,静态dom与动态dom的不同处理,缓存的使用以及块的作用。

致谢Vue Mastery非常好的课程,可以转载,但请声明源链接:文章源链接justin3go.com(有些latex公式某些平台不能渲染可查看这个网站)

编译器基本作用


<div>foo</div>


上面是源模板,下面是生成的渲染函数代码

{
    export function ssrRender(_ctx, _push, _parent) {
        _push(`<div>foo</div>`)
    }
}


有什么作用:在开发过程中,用这个来调试编译器,知道实际是怎么运行的;

静态优化选项


右上角的是一些优化选项,这里都是静态的,所以可以打开这个选项:

然后对应的编译代码也会发生变化:

以便在每次组件更新的时候可以在每个渲染器上重用它,一旦一个节点被提升,它就会被创建一次在渲染函数之外,在以后的每一次渲染中,它将在这里重新使用,两个好处:

  • 避免重新创建对象,然后扔掉(垃圾回收相关的知识)
  • 在模式算法中,当看到两个节点在同一位置时,在严格平等的情况下,可以跳过它,因为我们知道它永远不会改变

动态相关优化

这里绑定了click侦听器,编译器会生成一个补丁标志

通常使用简单的虚拟DOM渲染算法,不管有多少东西在div自身上

这整个对象必须作为一个整体来diff:

所以即使从模板中我们可以看到这个ID实际上是静态的,永远不会改变,我们还是会遍历整个对象,只是为了确保它不会改变,因为运行时没有足够的信息来知道这方面;

但是,使用Vue3的编译器,这个补丁和数组结合在一起,为运行提供足够的信息<表示有些props会改变,但唯一可能改变的是onClick,因为它适合暴露在外的东西结合在一起>

所以我们可以跳过此props上的对象枚举,忽略那些已经被编译器推断永远不会改变的props

总的,性能优化的方面做了许多的更新,在虚拟DOM中,当情况发生变化时,并没有检查整个节点的所有属性和元素,检查的是一些具体的东西通过添加类似的提示<确切地说,这些是编译器生成的提示,以帮助运行时更高效>

但是很多时候,你并不打算改变事件处理程序,所以有一个选项是默认打开的:

这里使用了一些智能JavaScript来缓存事件处理程序,这里将它变成了一个内联函数,并在第一次渲染时将其缓存,后续渲染就始终使用同一个内联处理程序了,所以我们总是传递相同的函数,但是这里面的函数会访问ctx.onClick

重点: 我们注意到补丁标志与onClick数组不见了,这意味着现在这个vnode当我们试图修补它的时候,它实际上并不需要被修补,因为这(id: “foo”)是静态的,而事件处理程序也已经被缓存,当被调用时,它总是指向最新的onClick,所以即使onClick下面发生了变化,我们不需要对vnode本身做任何事情,可以理解为vnode里面存的是指向,具体的程序存在缓存中,并且实时更新为最新的。所以,现在我们在修补过程中可以完全跳过整个节点。

这一点非常重要,因为在组件中,如果要将事件处理程序添加到组件中,会导致子组件不必要地重新渲染的最常见的情况之一是指使用类似内联事件处理程序

或者当你些foo的时候,你给它一个参数,这也是一个隐式的内联处理程序

所以这些在Vue2中,即使什么都没有改变,它仍然会导致子组件在父组件重新渲染时而重新渲染,在大型应用,这会引起连锁反应,因为你在向下传递函数,在每次渲染时,都会创建一个新的内联函数,会导致所有这些收到那个prop的子组件重新渲染;

所以在Vue3中使用处理缓存,极大地减少了在大型组件树中发生不必要的渲染

block有什么作用?


当根div被创建时,就像block一样:

假如有一个这样的临时结构:

在右边,我们可以看到它被提升了,_hoisted_1

想象这是一个手动写的非优化虚拟DOM树,在更新时,你要确保DOM结构是一致的,如果这是手动编写的,那么运行时就没有关于这个DOM树是否稳定的信息了,它不能做出仍任何假设因为节点顺序可能已经改变了或者div-->p,所以运行时需格外小心,它必须检查每个节点以确保它没有变成别的东西,如果有props的话就要把所有的props都区分开来确保props没有改变,而说孩子节点,事实上,它必须区分两个子数组,以确保它们没有四处走动或者没有新的孩子节点加入或删除。

<div>
    <div>
        <span>hello</span>
    </div>
</div>


会得到这样的结果:

function render(){
    return h('div', [
        h('div', [
            h('span', 'hello')
        ])
    ])
}


你可以想象最终的Json结构,底层数据结构可能是这个样子

const vdom = {
    tag: 'div',
    children: [
        {
            tag: 'div',
            children: [
                {
                    tag: 'span',
                    children: 'hello'  // 当这里变化为msg(#)
                }
            ]
        }
    ]
}


像这样的结构,更新时,它将有两个快照(新旧)

“#”部分: 如果我们不提供更多的提示,渲染器并不知道发生了什么变化,所以上述结构必须经过一个相对暴力的算法递归遍历整颗树,比较新旧;

对于中小型应用,这种方法并不会达到性能瓶颈,但是对于大型应用:当你点击某个东西时,可能你的应用程序会有10个组件同时被触发再更新,这就是JavaScript成本开始增加的时候,可能阻塞或卡顿。这时候人们就开始了解如何手动优化组件树来避免不必要的重新渲染,这就是Vue的优势:尝试让框架变得聪明--增加了一些提示以及如何实现这一目标的优化;

回到块的想法中,稍微修改一点变为一个好的例子,加入{{msg}}使整颗树变为动态的,使其不能被提升

理想情况下,我们知道这个div不会改变,唯一可能改变的就是这个span;

如果在其他地方添加一些不相关的节点

作为人类,我们可以很清楚的知道整颗DOM树只有span那个节点在变化,但是如果没有编译器生成的提示,虚拟DOM渲染器只看到JavaScript树,它并不知道哪个部分会改变,所以编译器的工作就是提供这些信息,运行的时候就知道可以跳过很多不必要的工作,这里就是直接到span这里

使用的方法就是block,将模板的根变成block

注意这里有一个openBlock调用,当块打开时,所有表达式、所有的孩子会被评估,这是在欺骗JavaScript,想法是当你创建这样一个节点时

因为它是动态的,它有我们称之为补丁标志的东西,应该被跟踪,当我们说tracked时,这个节点就会被添加到当前打开的block作为动态节点。所以整个调用之后,这个根div将有一个额外的属性称为动态子节点,它将只包含此节点,同时我们还有完整的结构通过正常的子层级,但是每个block都有一个额外的数组,只跟踪其中的动态节点,所以这个span标签可以无论多深,block将只跟踪动态节点在一个扁平数组中。

使用v-if等结构指令


可能改变节点的结构

当这个被切换时,整个div就会从树上消失,所以对于这个根block,它再也不能对此做出安全的假设了,相反,我们把这个完整的if部分变成一个块(它自己的)这个块被跟踪了,作为父块的动态子级。所以我们有嵌套的块,每个块将在扁平的数组中跟踪它自己的动态子对象

数个节点中可能只有几个这样的v-if和v-for,所以,我们基本上还是需要遍历block树。然而,大多数情况下是扁平数组迭代,而不是去diff和比较检查潜在的节点移动,所以效率更高,减少了递归的数量;

对于每个节点,补丁标志本身还编码了关于什么样的工作的信息,比如上面这个TEXT标志意味着:当你试图区分这个节点时,你只需检查它的文本内容,而不必考虑它的props。将所有这些结合起来,编译器将真正生成运行时渲染函数,它运行运行时利用所有的这些提示做尽可能少的工作



目录
相关文章
|
25天前
|
存储 JavaScript 前端开发
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
【10月更文挑战第21天】 vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
|
22天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
50 7
|
23天前
|
前端开发 数据库
芋道框架审批流如何实现(Cloud+Vue3)
芋道框架审批流如何实现(Cloud+Vue3)
41 3
|
22天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
39 1
|
22天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
44 1
|
25天前
|
前端开发 JavaScript
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
|
25天前
Vue3 项目的 setup 函数
【10月更文挑战第23天】setup` 函数是 Vue3 中非常重要的一个概念,掌握它的使用方法对于开发高效、灵活的 Vue3 组件至关重要。通过不断的实践和探索,你将能够更好地利用 `setup` 函数来构建优秀的 Vue3 项目。
|
28天前
|
JavaScript API
vue3知识点:ref函数
vue3知识点:ref函数
33 2
|
27天前
|
缓存 JavaScript 数据管理
优化 Vue 应用中的性能
【10月更文挑战第22天】优化 Vue 应用性能需要综合考虑多个方面,从数据管理、组件化、虚拟 DOM 操作、网络请求、代码结构等多方面入手,不断进行优化和改进,以提供更流畅的用户体验。同时,要根据具体的应用场景和需求,选择合适的优化策略,确保性能优化的效果和可行性。
|
28天前
|
JavaScript 前端开发 API
vue3知识点:Vue3.0中的响应式原理和 vue2.x的响应式
vue3知识点:Vue3.0中的响应式原理和 vue2.x的响应式
25 0
下一篇
无影云桌面