源码目录结构解析

下载并启动Vue 3源码

Vue 3 的源码地址可以在 GitHub 上下载(第 11 章采用的 Vue.js 版本为 3.2.20),首先安装 Git,然后执行如下命令:

git clone https://github.com/vuejs/vue-next.git

完成后,Vue 3 的源码被下载到 vue-next 文件夹下。打开 CMD 命令行工具,由于 Vue 3 源码采用 Yarn 进行构建,因此需要提前安装 Yarn,安装命令如下:

npm i yarn -g

然后进入 vue-next 目录,执行如下命令:

yarn --ignore-scripts

执行完该命令后,相关依赖就已经安装。执行 npm run dev 命令,即可开启 Vue 3 的源码调试模式,查看 vue-next\packages\vue\dist 目录下的 vue.global.js 文件,即开发模式下构建出的 Vue 3 源码文件,源码完整目录结构如图 11-1 所示。

image 2024 02 24 15 48 41 740
Figure 1. 图11-1 Vue 3 源码目录结构

其中,Vue 3 的核心源码都在 packages 中,并且是基于 Rollup 构建的,其中每个目录的含义如下:

├── packages
│   ├── compiler-core     // 核心编译器(平台无关)
│   ├── compiler-dom      // DOM编译器
│   ├── compiler-sfc      // Vue单文件编译器
│   ├── compiler-ssr      // 服务端渲染编译
│   ├── global.d.ts       // TypeScript声明文件
│   ├── reactivity        // 响应式模块,可以与任何框架配合使用
│   ├── runtime-core      // 运行时核心实例相关代码(平台无关)
│   ├── runtime-dom       // 运行时dom 相关API、属性、事件处理
│   ├── runtime-test      // 运行时测试相关代码
│   ├── server-renderer   // 服务端渲染
│   ├── sfc-playground    // 单文件组件在线调试器
│   ├── shared            // 内部工具库,不对外暴露API
│   ├── size-check        // 简单应用,用来测试代码体积
│   ├── template-explorer // 用于调试编译器输出的开发工具
│   └── vue               // 面向公众的完整版本,包含运行时和编译器

目录模块

通过上面的源码结构,可以看到有下面几个模块比较特别:

  • compiler-core

  • compiler-dom

  • runtime-core

  • runtime-dom

可以看到 coredom 分别出现了两次,那么 compilerruntime 又有什么区别呢?

  • compile:可以理解为程序编译时,是指我们写好的源代码在被编译成为目标文件这段时间,在这里可以理解为我们将 .vue 文件编译成浏览器能识别的 .js 文件的一些工作。

  • runtime:可以理解为程序运行时,即程序被编译了之后,在浏览器打开程序并运行它,直到程序关闭的这段时间的系列处理。

package 目录下,除了上面列举的 4 个目录外,reactivity 目录也比较重要,它是响应式模块的源码,由于 Vue 3 整体源码采用的是 Monorepo 规范,因此其下面每个子模块都可以独立编译和打包,从而独立对外提供服务,在使用时采用 require('@vue/reactivity') 引入,进入 reactivity 目录下可以看到有对应的 package.json 文件。

其他目录:compiler-sfc 是一个单文件组件编译工具;server-renderer 是服务端渲染模块的源码;sfc-playground 是一个在线 Vue 单文件组件调试工具;shared 包括常用的工具库方法的源码;size-check 是一个工具,可以用来测试代码的体积;template-explorer 是用于调试编译器输出的开发工具;最后的 vue 则是 Vue.js 的完整源码产生目录。

构建版本

通常构建出来的 Vue 会有 vue.global.jsvue.runtime.global.js 两种版本,其中:

  • vue.global.js:包含编译器和运行时的 “完整” 构建版本,因此它支持动态编译模板。

  • vue.runtime.global.js:只包含运行时,并且需要在构建步骤期间预编译模板。

其中,如果需要在客户端编译模板(即将字符串传递给 template 选项,或者使用元素的 DOM 内的 HTML 作为模板挂载到元素),则需要编译器,因此需要完整的构建版本,代码如下:

// 需要编译器
Vue.createApp({
    template: '<div>{{ hi }}</div>'
})
// 不需要
Vue.createApp({
    render() {
        return Vue.h('div', {}, this.hi)
    }
})

当使用 Webpackvue-loader 时,.vue 文件中的模板会在构建时预编译为 JavaScript,在最终的捆绑包中并不需要编译器,因此可以只使用运行时构建版本。所以,如果直接在浏览器打开 Vue 的页面,则可以直接采用 <script> 引入完整版本(正如之前章节中的示例代码一样),如果采用构建工具(例如 Webpack)进行构建,则可以使用 import 引入运行时版本,和构建相关的脚本源码都在 vue-next/scripts 下面。例如,当我们需要构建出完整版时,可以在 vue-next 根目录执行,或者修改 package.jsondev 命令:

node scripts/dev.js -f global-runtime

当需要构建运行时版本时,则执行:

node scripts/dev.js -f global

具体的 -f 参数以及其他参数配置的含义可以在 vue-next/rollup.config.js 里面找到。

执行完成后,在 vue-next\packages\vue\dist 目录下可以得到对应的文件。所有 Vue 3 可构建出来的版本如下:

// cjs(用于服务端渲染)
vue.cjs.js
vue.cjs.prod.js(生产版,代码进行了压缩)
// global(用于浏览器<script src="" />标签导入,导入之后会增加一个全局的Vue对象)
vue.global.js
vue.global.prod.js(生产版,代码进行了压缩)
vue.runtime.global.js
vue.runtime.global.prod.js(生产版,代码进行了压缩)
// browser(用于支持ES 6 Modules浏览器<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(这两个版本没有打包所有的代码,只会打包使用的代码,需要配合打包工具来使用,会让Vue体积更小)
vue.esm-bundler.js
bue.runtime.esm-bundler.js

不同构建版本的 Vue 源文件需要在不同的平台和环境中使用,便于开发者选择合适的场景来使用。