源码笔记(二):源码结构及调试介绍
项目准备
拉取 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 里,后续分析将会在那个文件里进行。