一文看懂Vue.js 3.0 的优化

167 阅读3分钟

一文看懂Vue.js 3.0 的优化

Vue 从 1.x 到 2.0 版本,最大的升级就是引入虚拟 DOM 的概念,它为后续做服务端渲染以及跨端框架 Weex 提供了基础。 Vue 2.x 的痛点:源码自身的维护性,数据量大后带来的渲染和更新的性能问题,一些想舍弃但为了兼容一直保留的鸡肋 API 等。 优化方向:更好的 TS 支持、更好的逻辑复用实践,也就是从源码、性能和语法 API 三个大的方面优化框架。

源码优化

目的:让代码更易于开发和维护。 方式:使用 monorepo 和 TypeScript 管理和开发源码,以此来提升代码可维护性。

更好的代码管理方式:monorepo

Vue2的源码托管在 src 目录,依据功能拆分出了

compiler(模板编译的相关代码)
core(与平台无关的通用运行时代码)
platforms(平台专有代码)
server(服务端渲染的相关代码)
sfc(.vue 单文件解析相关代码)
shared(共享工具代码)

Vue3.0 中,整个源码通过 monorepo 的方式维护,根据功能将不同的模块拆分到 packages 目录下面不同的子目录 通过 monorepo 将模块拆分到不同的 package 中,每个 package 有各自的 API、类型定义和测试。这样使得模块拆分更细化、职责划分更明确,模块之间的依赖关系也更加明确,开发人员也更容易阅读、理解和更改所有模块源码,提高代码的可维护性。

另外一些 package 是可以独立于 Vue 使用的,这样用户如果只想使用某个特性,可以单独依赖特性库而不用去依赖整个 Vue.js,减小了引用包的体积大小。而 Vue2.x 是做不到这一点的。

有类型的 JavaScript:typescript

Vue 2.x 使用 Flow,可是 Flow 烂尾了,类型判断不是很准确

Vue 3.0 使用 TS 重构了整个项目,TS 提供了更好的类型检查,能支持复杂的类型推导。

性能优化

源码体积优化

首先,移除了一些冷门的 feature(比如 filter、inline-template等);

其次,引入 tree-shaking 的技术,减少打包体积。

tree-shaking 的原理

tree-shaking 依赖 ES2015 模块语法的静态结构(即 import 和 export),通过编译阶段的静态分析,找到没有引入的模块并打上标记。在压缩阶段会利用例如 uglify-js/terser 等压缩工具真正地删除这些没有用到的代码。

数据劫持优化

在Vue 中,DOM 是数据的一种映射,数据发生变化后可以自动更新 DOM,用户只需要专注于数据的修改,没有其余的心智负担。

Vue 如何知道更新哪一片 DOM 呢?

在渲染 DOM 的时候访问了数据,Vue 对它进行了访问劫持,这样就在内部建立了依赖关系,也就知道数据对应的 DOM 是什么了。

访问劫持的实现方式

Vue1.x 和 Vue2.x 内部都是通过 Object.defineProperty 这个 API 去劫持数据的 getter 和 setter

    Object.defineProperty(data, 'a', {
        get() {
        },
        set() {
        }
    })

缺陷1:必须预先知道要拦截的 key 是什么,所以它并不能检测对象属性的添加和删除。

Vue 为了解决这个问题提供了 setset 和 delete 方法。

缺陷2:要劫持嵌套层级比较深的对象,就得递归遍历这个对象,执行 Object.defineProperty 把每一层对象数据都变成响应式的。

export default {
  data: {
    a: {
      b: {
        c: {
          d: 1
        }
      }
    }
  }
}

Vue3 使用了 Proxy API 做数据劫持:

    observed = new Proxy(data, {
      get() {
        // track
      },
      set() {
        // trigger
      }
    })

由于它劫持的是整个对象,对于对象的增加和删除都能检测到。