源码笔记(二):源码结构及调试介绍
项目准备
拉取 vue
代码到本地仓库,并切换版本到 v2.6.11
:
git clone https://github.com/vuejs/vue.git
git checkout v2.6.11
进入 vue
文件夹根目录下安装依赖:
yarn
目录结构
忽略掉 .babelrc.js,.editorconfig
等常规项目文件,只介绍 vue
相关。
├─ 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
方法解析。
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
开始调试
vue
采用 rollup 作为构建工具。
修改 npm scripts
里的 dev
字段对应的命令,在命令后面增加 --sourcemap
,然后执行:
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
,该文件里面引入:
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
分析,继续引入:
import { compileToFunctions } from './compiler/index';
该文件里执行:
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
里,后续分析将会在那个文件里进行。