Skip to content
On this page

Vue3 重大更新

Vue.js 3 是 Vue.js 框架的下一个重大版本,它带来了许多重大更新和改进。

更快的渲染性能

Vue.js 3 在虚拟 DOM 的实现上进行了一系列优化,旨在提高渲染性能和减少不必要的开销。

  1. 静态树提升(Static Tree Hoisting):

静态树(Static Tree)是指在组件树中不会发生变化的部分。

静态树提升(Static Tree Hoisting)是一种优化技术,它通过静态分析组件树,在编译阶段将静态的子树提升为单独的组件。这意味着在每次更新时,这些静态子树不需要进行虚拟 DOM 对比和渲染操作,从而减少了不必要的计算和渲染开销。

比如常见的静态文本组件、图片组件、静态导航栏组件等。这些组件中的内容在每次更新时都保持不变,因此它们可以被识别为静态树,并通过静态树提升优化技术来避免不必要的对比和渲染操作,从而提升渲染性能。

  1. 基于 Proxy 的观察者机制:

使用基于 Proxy 的观察者机制替代了 Vue.js 2 中的基于 Object.defineProperty 的观察者机制。这种新的观察者机制在响应式系统中更高效,减少了对属性的劫持和追踪的开销。

  1. 缓存事件处理函数:

在编译过程中,会对事件处理函数进行缓存,避免了每次重新创建函数的开销。这样可以减少了事件处理的时间和开销。

在 Vue.js 3 中,针对事件处理函数的缓存是通过编译器的优化来实现的。Vue.js 的编译过程将模板转换为渲染函数,这个过程中会对模板中的事件处理函数进行静态分析和优化。

具体来说,当编译器遇到模板中的事件处理函数时,它会生成一个标识符(identifier)来表示该事件处理函数,并将该标识符与实际的事件处理函数进行关联。这个标识符是一个编译时的静态值,与实际的事件处理函数无关。

当组件被渲染时,实际的事件处理函数会被创建并与之前生成的标识符进行关联。这个过程中,Vue.js 会使用一种叫做 Patch Flag 的技术来跟踪组件更新中的修改点。如果事件处理函数没有发生变化,Vue.js 会复用之前生成的标识符,从而避免了重复创建事件处理函数的开销。

通过对事件处理函数的缓存,Vue.js 3 在每次组件更新时可以减少对事件处理函数的创建和销毁,提高了渲染的效率。这对于包含大量事件处理函数的组件特别有用,可以显著降低不必要的计算和内存开销。

需要注意的是,事件处理函数的缓存仅适用于静态的事件处理函数,即在组件的声明周期内不会发生变化的事件处理函数。如果事件处理函数是动态生成的或会在组件的声明周期中改变,那么缓存的优化机制就不会生效。

  1. 静态节点标记:

在虚拟 DOM 中标记静态节点,即那些在每次更新时不会改变的节点。这样可以避免对比和渲染静态节点,提高了渲染性能。

静态节点标记(Static Node Marking)是另一种优化技术,它在虚拟 DOM 中标记静态节点,即那些在每次更新时不会改变的节点。通过标记静态节点,Vue.js 可以避免对比和渲染这些静态节点,从而提高渲染性能。

  1. Patch Flag 和 Block Tracking:

Patch Flag(补丁标记)和 Block Tracking(块跟踪)是 Vue.js 3 中用于跟踪组件更新的技术,旨在提高渲染的效率和性能。

Patch Flag 是一种用于标记组件更新的信息的位掩码。它存储在虚拟 DOM 节点的元信息中,并用于在进行组件更新时快速确定需要执行的操作。Patch Flag 的不同位表示了不同的更新类型,例如是否需要更新子组件、是否需要更新 props、是否需要更新文本内容等。通过 Patch Flag,Vue.js 可以快速地确定需要进行哪些具体的 DOM 操作,从而避免不必要的计算和比较,提高了渲染的效率。

Block Tracking 是一种用于跟踪模板块(template block)的技术。在 Vue.js 3 中,编译器将模板分为多个块,每个块是一组连续的节点。Block Tracking 会为每个块生成一个唯一的标识符,并将该标识符与实际的 DOM 节点关联起来。在组件更新时,Vue.js 可以通过比较前后的块标识符,快速判断哪些块需要进行更新,从而避免不必要的比较和渲染操作。


当然,虚拟DOM Diff算法的优化也是渲染性能提升的重要因素。包括以下几点:

静态标记:同上

静态提升:同上

Fragments(片段):引入了 Fragments 的概念,允许组件返回多个根节点,而不需要包裹在额外的 DOM 元素中。这样可以减少额外的 DOM 元素节点,减小生成的 Virtual DOM 树的大小,提高渲染性能。

静态 Inlines:通过静态 Inlines 的优化技术,将静态节点的属性直接内联到 Virtual DOM 中,避免了属性的 diff 过程,减少了 Virtual DOM 操作的开销。

更小的包体积

Vue.js 3 的包体积比 Vue.js 2 更小。它采用了模块化的设计,允许按需引入功能模块,减少了打包后的文件大小。

  1. 重构和优化:

对内部代码进行了重构和优化,精简了不必要的代码和依赖,提高了整体的效率和性能。

重写了响应式系统,采用了基于 Proxy 的实现方式。相比于 Vue.js 2 的基于 Object.defineProperty 的实现方式,Proxy 可以更好地处理动态添加和删除属性的情况,并且在性能方面也有所提升。

对编译器进行了优化,改进了生成的渲染函数的代码质量和效率。通过编译时的静态分析和优化,减少了运行时的开销,并且生成的代码更加精简。

  1. 模块化架构:

使用了更加模块化的架构,将核心功能拆分为多个独立的模块。通过 npm 包管理工具进行安装和导入,开发者可以根据自己的需求选择性地导入所需的模块,而不需要导入整个 Vue.js 的库。这种模块化架构使得应用程序更加灵活和可定制,减小了包体积,并提高了开发效率。

  1. Tree-shaking:

Tree-shaking(摇树优化)是一种在打包过程中剔除未使用代码的优化技术。它通过静态分析代码的依赖关系,将未被引用的代码从最终的构建包中移除,从而减小包的体积。

Tree-shaking 的作用是减少最终打包出来的代码量,去除未使用的功能和模块,提高应用的加载速度和运行性能。它可以显著减小包的体积,减少网络传输的数据量,加快应用的初始化时间,提升用户体验。

Tree-shaking 的原理是基于 ES6 模块系统的静态解析能力。当代码使用 ES6 模块的语法进行导入和导出时,打包工具(如Webpack、Rollup等)可以通过静态分析代码的依赖关系,确定哪些模块没有被引用到,从而将它们从最终的构建包中排除掉。

要实现 Tree-shaking,需要满足以下条件:

  • 使用 ES6 模块的语法进行模块的导入和导出,而不是采用其他模块化方式(如CommonJS)。

  • 代码中的导入必须是静态的,即不能使用动态导入的方式,因为动态导入的模块在静态分析时无法确定。

javascript
// 静态导入的示例
import { foo } from './utils';

// 动态导入的示例(非静态)
const condition = Math.random() > 0.5;
if (condition) {
  import('./moduleA').then(moduleA => {
    // 根据条件动态导入模块
    moduleA.foo();
  });
}
  • 代码的依赖关系必须是清晰的,没有复杂的条件分支和动态的模块加载,以便打包工具可以静态分析代码的引用关系。

通过合理使用 ES6 模块的语法,结合现代的打包工具,开发者可以充分利用 Tree-shaking 技术,剔除未使用的代码,减小包的体积,提高应用的性能和效率

  1. 编译时优化:

在编译阶段进行了优化,通过静态分析模板,并对生成的渲染函数进行优化。这包括静态节点标记、静态树提升等技术,可以减少运行时的开销,从而减小了最终的包体积。

  1. 组件按需加载:

支持组件的按需加载。组件按需加载是指在需要使用某个组件时,才将该组件的代码动态加载到应用中,而不是在应用初始化时一次性加载所有组件的代码。这样可以减小初始加载的包体积,提升应用的性能和加载速度。

javascript
import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'Home',
      component: () => import('./components/Home.vue')
    },
    {
      path: '/about',
      name: 'About',
      component: () => import('./components/About.vue')
    }
  ]
});

export default router;

Composition API

Composition API是Vue.js 3.0中引入的一种新的API风格,它提供了一种更灵活、可组合的方式来组织和重用Vue组件的逻辑代码。传统的Options API将组件的选项(如data、methods、computed等)放在一个对象中,而Composition API则将组件的逻辑代码拆分为更小的函数,并允许开发者按需组合这些函数,以实现更好的代码复用和组织。

下面是Composition API的一些核心概念和特性:

  1. setup函数:

每个使用Composition API的组件都必须包含一个名为"setup"的函数。在setup函数中,可以定义响应式数据、计算属性、方法等,并返回一个对象,该对象中的属性和方法将被暴露给组件模板中使用。

  1. Reactive API:

提供了一组新的函数,如reactive、ref、computed等,用于创建和管理响应式数据。reactive函数用于创建一个包含响应式数据的对象,ref函数用于创建一个包装基本类型的响应式数据。computed函数用于创建计算属性。

  1. 组件逻辑复用:

使得组件的逻辑代码可以更方便地复用。通过将逻辑代码封装为函数,可以将这些函数在不同的组件之间进行组合和共享。这种方式比mixin更灵活,避免了命名冲突和隐式依赖的问题。

  1. 生命周期钩子:

提供了一组新的生命周期钩子函数,如onBeforeMount、onMounted、onBeforeUpdate、onUpdated等。这些钩子函数可以在setup函数中使用,以便更好地控制组件的生命周期。

  1. 自定义函数:

还支持自定义函数,使得开发者可以根据需要定义和使用自己的逻辑代码。这样可以进一步提高代码的可读性、可维护性和复用性。

更强大的响应式系统:

响应式系统在性能和功能方面得到了改进。它使用了基于 Proxy 的观察者机制,提供了更细粒度的响应式能力,并且对于嵌套对象和数组的处理更加高效和准确。

更好的 TypeScript 支持:

对TypeScript 的支持更加完善,包括更好的类型推导、类型检查和 IDE 支持。这使得在使用 TypeScript 开发 Vue.js 应用程序时更加流畅和可靠。

新的生命周期钩子:

引入了一些新的生命周期钩子函数,如 beforeMount 和 beforeUnmount,以提供更精细的组件生命周期控制和更好的性能优化机会。

细节可以看下一篇文章,vue的生命周期,详细的讲解了vue2和vue3的生命周期。