Skip to content

源码笔记(二):源码结构及调试介绍 ​

项目准备 ​

拉取 vue 代码到本地仓库,并切换版本到 v2.6.11:

bash
git clone https://github.com/vuejs/vue.git
git checkout v2.6.11

进入 vue 文件夹根目录下安装依赖:

bash
yarn

目录结构 ​

忽略掉 .babelrc.js,.editorconfig 等常规项目文件,只介绍 vue 相关。

js
├─ benchmarks                 // 基准数据,一些测试性能的 demo,用于与竞品框架对比
├─ dist                       // 构建后输出的不同版本的 Vue 文件
├─ examples                   // 用 Vue 写的一些小 demo
├─ flow                       // flow 静态类型声明文件
├─ packages                   // 构建后输出的服务端渲染、模板编译器、weex 相关的NPM包
├─ scripts                    // 存放 npm scripts 执行的各种 script,用于项目编译、测试、构建。
├─ src                        // 核心
│   ├─ compiler               // 编译器相关,用于解析模版,template->render()
│       ├─ codegen            // AST 转换为 render()
│       ├─ directives         // 生成 render() 之前需要处理的指令
│       ├─ parser             // template 解析为 AST
│       ├─ codeframe.js       // 导出 generateCodeFrame,用于格式化 console template
│       ├─ create-compiler.js // 导出 createCompilerCreator,用于返回 createCompiler
│       ├─ error-detector.js  // 检查 AST 的错误
│       ├─ helpers.js         // 一些编译的帮助方法
│       ├─ index.js           // 导出 createCompiler 方法,用于返回 compile 和 compileToFunctions
│       ├─ optimizer.js       // 标记静态节点,用于重建优化
│       └─ tofunction.js      // 导出 createCompileToFunctionFn,用于返回 compileToFunctions
│   ├─ core                   // 核心代码,包括内置组件,全局API封装,Vue 实例化,观察者,虚拟DOM, 工具函数等等。
│       ├─ components         // 内置组件定义,目前包含 Keep-Alive
│       ├─ global-api         // 全局 api 定义,如 Vue.component,Vue.use,Vue.extend,Vue.mixin等
│       ├─ instance           // 实例化相关内容,生命周期定义、事件等
│       ├─ observer           // 数据监听,双向数据绑定,订阅中心设置等
│       ├─ util               // 工具方法
│       ├─ vdom               // 虚拟DOM相关
│       ├─ config.js          // 基础配置
│       └─ index.js           // 导出 Vue
│   ├─ platforms              // 跨平台相关
│       ├─ web                // web端
│           ├─ compiler       // 创建 createCompiler 所需参数 baseOptions,导出compile,compileToFunctions
│           ├─ runtime        // 在Vue上挂载了一些新的directives,components,config,__patch__,$mount(重写)等
│           ├─ server         // 服务端渲染
│           ├─ util           // 工具方法
│           └─ xxx.js         // 5个入口js,不同构建调用不用的方法
│       └─ weex               // weex相关
│   ├─ server                 // 服务端渲染(ssr)
│   ├─ sfc                    // 单文件组件解析(*.vue)
│   └─ shared                 // 全局共享的常量,方法
├─ test                       // 测试用例
├─ types                      // typescript 类型声明文件

针对目录的一些解释 ​

dist ​

dist 下有 10 几种不同版本的 vue 文件,他们是根据 不同规范(包括 CommonJS 规范,ES Module,UMD) 和 是否包含编译器 和 不同环境 构建出的不同版本。

具体查阅 文档

flow ​

类似 TypeScript,Flow 是 facebook 出品的 JavaScript 静态类型检查工具。Vue.js 的源码利用了 Flow 做了静态类型检查。

packages ​

通过执行 npm script 对应的命令可编译出以下 npm 包。

vue-server-renderer ​

Vue.js 服务器端渲染(SSR) 所用。

vue-template-compiler ​

vue-template-compiler 通常与 vue-loader 连用,用于将单文件组件 (SFCs)预编译为渲染函数。

vue-template-compiler 作为 compiler 在 vue-loader/lib/index.js 的 parse 方法里传入,(其中会调用 compiler.parseComponent)得到 descriptor,根据 descriptor 可以生成不同模块的 import 请求,然后通过 pitcher 将原 import 请求转化为新的 import 请求,然后根据新的 import 请求执行 js,转化不同模块,其中转化 template 执行 templateLoader.js 时会去依赖 vue-template-compiler 提供 compiler.compile 方法解析。

vue-loader 解析

weex-template-compiler ​

weex 相关(略过)

weex-vue-framework ​

weex 相关(略过)

weex ​

Weex 是使用流行的 Web 开发体验来开发高性能原生应用的框架,集成的是运行时版本的 Vue。

加载流程参考:Weex 加载流程,浅说 Weex 工作原理

sfc ​

sfc 即单文件组件(Single File Components),执行 sfc 下的 parse.js 里导出的 parseComponent 方法得到 SFCDescriptor 对象。该文件最终会打包到 vue-template-compiler 里。

test ​

包含 karma 和 jasmine 两种测试工具。

开始调试 ​

vue 采用 rollup 作为构建工具。

修改 npm scripts 里的 dev 字段对应的命令,在命令后面增加 --sourcemap,然后执行:

bash
npm run dev

此时会在 dist 生成 vue.js 和 vue.js.map,然后在在任意处新增 html 文件,然后 script 标签引入刚生成的 vue.js,然后书写 vue 代码在浏览器打开即可断点到源代码而不是打包后的代码。

程序入口 ​

dev 命令对应的 TARGET 为 web-full-dev,即对应的平台入口为 platforms/web/entry-runtime-with-compiler.js,该文件里面引入:

js
import Vue from './runtime/index';

该文件会从 core/index(他又会从 ./instance/index 引入Vue,该文件里会初始化 Vue 的各项,通过传入 Vue 作为参数的方式(Mixin)扩展原型) 里引入 Vue,并进行平台化相关的定制,如扩展了 Vue.options.directives、Vue.options.components。 初始化了 Vue.prototype.__patch__ 为 patch 方法,初始化了 Vue.prototype.$mount,该 $mount 会调用 core/instance/lifecycle 里的 mountComponent。

回到 entry-runtime-with-compiler.js 分析,继续引入:

js
import { compileToFunctions } from './compiler/index';

该文件里执行:

js
const { compile, compileToFunctions } = createCompiler(baseOptions);

其中 createCompiler 为 compiler/index 的导出,用于创建编译器。这里传入平台相关的参数 baseOptions 得到对应的 compile,compileToFunctions。compileToFunctions 用于将 template 转化为 render 函数。

回到 entry-runtime-with-compiler.js 继续分析,重写了 Vue.prototype.$mount,并在方法里末尾执行了原 $mount,然后将 compileToFunctions 赋给了 Vue.compile,最后导出 Vue。

demo 分析使用编译后的 vue ​

因为 demo 里会涉及到组件引入和其他模块,所以采用 webpack 打包,将 vue 作为 npm 包引入,所以意味着断点将会打到编译后的 vue 的 js 里,后续分析将会在那个文件里进行。