源代码组织方式

  • 源码使用 TypeScript 重写
  • 使用 Monorepo 管理项目结构
    使用一个项目管理多个包,把不同功能的代码放到不同的包,每一个功能模块划分明确,模块的依赖也很明确,每个功能模块都可以单独测试,单独发布,单独使用。

Vue 3 packages 目录结构:
image.png
compiler-core:和平台无关的编译器
compiler-dom:浏览器平台的编译器
compiler-sfc:单文件组件编译器,依赖于 compiler-core、compiler-dom
compiler-ssr:服务端渲染编译器,依赖于 compiler-dom
reactivity:数据响应式系统
runtime-core:和平台无关的运行时
runtime-dom:浏览器运行时
runtime-test:测试用的轻量运行时,渲染出来的 DOM 树是一个 JS 对象,可以在任何 JS 环境运行
server-renderer:服务端渲染
shared:公共 API
size-check:检查包的大小,私有包
template-explorer:浏览器运行的实时编译组件,编译输出 render 函数
vue:打包完整版的 vue

构建版本

packages/vue:
image.png

  • cjs:CommonJS 版本
    • vue.cjs.js:开发版
    • vue.cjs.prod.js:生产版,压缩版
  • global:全局版,完整版,可以通过 script 标签直接导入页面,导入后创建一个全局 Vue 对象
    • vue.global.js:开发版
    • vue.global.prod.js:生产版,压缩版
    • vue.runtime.global.js:只包含运行时的开发版
    • vue.runtime.global.prod.js:只包含运行时的生产版
  • browser:浏览器版本,可以通过<script type="module" src="...">导入
    • vue.esm-browser.js:浏览器完整开发版
    • vue.esm-browser.prod.js:浏览器完整生产版
    • vue.runtime.esm-browser.js:只包含运行时的浏览器开发版
    • vue.runtime.esm-browser.prod.js:只包含运行时的浏览器生产版
  • bundler:内部导入了 runtime-core 模块,需要配合 ES Modules 打包工具,只会打包用到的代码
    • vue.esm-bundler.js:完整版的,内部还导入了 runtime-compiler,也就是编译器
    • vue.runtime.esm-bundler.js:只包含运行时,是 Vue 的最小版本

Composition API

Vue 2 使用的代码组织方式是 Options API。

Options API

创建包含一个描述组件选项(data、methods、props等)的对象。
缺点是开发复杂组件的时候,同一个功能逻辑的代码被拆分到不同选项中
image.png

Composition API

Vue 3 新增的一组 API,它是基于函数的 API,使得开发者可以更灵活的组织组件的逻辑。
image.png

性能优化

响应式系统升级

Vue 2 中,响应式原理的实现是通过 defineProperty 函数,遍历对象的属性,转成 getter/setter,如果属性的值又是一个对象,也要递归进行响应式处理。
Vue 3 实现响应式的核心是 ES6 新增的 Proxy 对象。
Proxy 对象本身的性能就比 defineProperty 好,并且可以拦截对对象的访问和修改,不需要在初始化时对所有属性进行遍历。
Proxy 对象可以监听到动态新增的属性,而 Vue 2 中要实现需要通过 Vue.set 等 API。
Proxy 对象可以监听到删除的属性,可以监听数组的索引和 length 属性,这是 Vue 2 做不到的。

编译优化

Vue 2 的编译过程,首先把模板编译成 render 函数,这个是在构建的过程中完成的。编译的时候,会先编译静态根节点和静态节点,静态根节点要求有一个静态子节点。当组件的状态发生变化,通知 Watcher,调用 Watcher 的 update,最后执行虚拟 DOM 的patch操作,遍历所有虚拟节点,寻找差异,最终把差异更新到 DOM 树上。diff 的过程中,会去比较整个虚拟 DOM,先比较新旧的 div,以及它的属性,再对比里面的子节点。
Vue 2 中通过标记静态根节点,来跳过静态根节点,优化 diff 的过程。但是静态节点没有跳过。
Vue 3 标记所有的静态节点,来跳过所有静态节点,diff 的过程只对比动态内容节点。
Vue 3 还引入了 Fragments,片段的特性,模板中不需要再像 Vue 2 那样创建一个 div 元素作为一个唯一的根节点,Vue 3 可以在 template 标签中写任何内容。
Vue 3 能够缓存事件处理函数,减少不必要的更新操作。

体积优化

  • 移除了一些不常用的 API
    • inline-template、filter等
  • 优化了 Tree-Shaking,找到没有引用的模块并过滤掉。一些没有用到的 API 和指令不会被打包。

Vite

浏览器加载 ESM

现代浏览器加载模块的方式:
<script type="module" src="" />

模块是默认延迟加载的,类似于设置 defer 关键字。在文档解析完成后,触发 DOMContentLoaded 事件前执行的。

Vite

Vite 是一个新的官方 CLI,Vite 对比 Vue-Cli 更快,因为它利用了浏览器能加载模块的特性,在开发环境下不需要打包就可以直接运行。Vue-Cli 则是在开发环境下也需要打包后才能运行。
当浏览器遇到导入 .vue 文件这种请求,Vite 会截取下来,调用 compiler-sfc,把组件编译成 render 函数再交给浏览器去渲染。也就是说,vite 编译组件是按需编译的。
Vite 支持快速冷启动,也支持模块热更新。
Vite 生产环境下使用的打包工具是 Rollup,基于 ES Module 的方式打包。Vue-Cli 使用 webpack,需要把 import 转换成 webpack_require,以及生成其它一些辅助函数。