Vue.js源码分析(响应式、虚拟DOM、模板编译和组件化)
Vue.js源码剖析-响应式原理
一、准备工作
1. Vue源码的获取
- 项目地址:https://github.com/vuejs/vue
- Fork一份到自己仓库,克隆到本地,可以自己写注释提交到GitHub
- 为什么分析Vue2.6
- 到目前为止Vue3.0的正式版还没有发布
- 新版本发布后,现有项目不会升级到3.0,2.x还有很长的一段过渡期
- 3.0项目地址:http://github.com/vuejs/vue-next
2. 源码目录结构
src
|——compiler 编译相关
|——core Vue核心库
|——platforms 平台相关代码
|——server SSR、服务器端渲染
|——sfc .vue 文件编译为js对象
|——shared 公共的diamante
3. 了解Flow
- 官网:https://flow.org/
- JavaScript的静态类型检查器
- Flow的静态类型检查错误是通过静态类型推断实现的
- 文件开头通过
// @flow
或者/* @flow */
声明
- 文件开头通过
/* @flow */
function square(n: number): number {
return n * n
}
square("2") // Error!
4. 调试设置
(1). 打包
-
打包工具Rollup
- Vue.js源码的打包工具使用时Rollup,比Webpack轻量
- Webpack把所有文件当做模块,Rollup只处理js文件,更适合在Vue.js这样的库中使用
- Rollup打包不会生成冗余的代码
-
安装依赖
yarn
-
设置sourcemap
- package.json文件中的dev脚本中添加参数 --sourcemap
"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev"
-
执行dev
yarn dev
执行打包,用的是Rollup,-w参数是监听文件的变化,文件变化自动重新打包- 结果: 生成了dist目录
-
执行build
yarn build
生成了不同版本的Vue
二、 Vue的不同构建版本
1. Vue的不同构建版本
npm run build
重新打包所有文件- 官方文档-对不同构建版本的解释
- dist/README.md
|| UMD | CommonJS | ES Module (基于构建工具使用) | ES Module (直接用于浏览器) | |
| :---------------------------- | :----------------- | :--------------------------- | :------------------------- | ---------------------- |
| 完整版 | vue.js | vue.common.js | vue.esm.js | vue.esm.browser.js |
| 只包含运行时版 | vue.runtime.js | vue.runtime.common.js | vue.runtime.esm.js | - |
| 完整版 (生产环境) | vue.min.js | - | - | vue.esm.browser.min.js |
| 只包含运行时版 (生产环境) | vue.runtime.min.js | - | - | - |
2. 术语
- 完整版:同时包含编译器和运行时的版本。
- 编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码,体积大,效率低。
- 运行时:用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码。体积小,效率高,基本上就是除去编译器的其它一切。
- UMD:通用的模块版本,支持多种模块方式。UMD 版本可以通过
<script>
标签直接用在浏览器中。jsDelivr CDN 的 https://cdn.jsdelivr.net/npm/vue 默认文件就是运行时 + 编译器的 UMD 版本 (vue.js
)。 - CommonJS:CommonJS 版本用来配合老的打包工具比如 Browserify 或 webpack 1。这些打包工具的默认文件 (
pkg.main
) 是只包含运行时的 CommonJS 版本 (vue.runtime.common.js
)。 - ES Module:从 2.6 开始 Vue 会提供两个 ES Modules (ESM) 构建文件:
vue脚手架创建的Vue项目中,引用的Vue版本就是运行时版本:vue.runtime.esm.js
我们可以在Vue项目中执行:
vue inspect > output.js
,将webpack配置输出到output.js文件中查看,resolve: { alias: { '@': '/Users/mac/JALProjects/lagou-fed/fed-e-task-03-02/code/01-demo/src', vue$: 'vue/dist/vue.runtime.esm.js' }, }
三、 寻找入口文件
查看dist/vue.js的构建过程
执行构建: yarn dev
四、 从入口开始
src/platform/web/entry-runtime-with-compiler.js
通过查看源码解决下面问题:
1. 问:观察一下代码,通过阅读源码,回答在页面上输出的结果
const vm = new Vue({
el: '#app',
template: '<h1>Hello template </h1>',
render (h) {
return h('h4', 'Hello render')
}
})
答:是优先使用render
2. 问:$mount是在什么时候调用的?
答:是在init方法中调用的,可以在$mount函数定义的地方打断点,然后看调用栈的函数
3. 学习源码记录
- el不能是body或者是html标签
- 如果没有render,把template转换成render函数
- 如果有render方法,直接调用mount挂载DOM
4. 调试代码
-
调试的方法
- 查看浏览器开发人员工具里Sources的CallStack调用堆栈
Vue的构造函数在哪个?
Vue实例的成员/Vue的静态成员从哪里来的?
五、Vue的初始化过程
1. 四个导出Vue的模块
(1). src/platforms/web/entry-runtime-with-compilerjs
-
web平台相关的入口
-
重写了平台相关的
-
$mount()
方法 -
注册了Vue.compile()方法,传递一个HTML字符串返回render函数
(2). src/platforms/web/runtime/index.js
- web平台相关
- 注册和平台相关的全局指令:
v-model
、v-show
- 注册和平台相关的全局组件:
v- transition
,v-transition-group
- 全局方法:
_patch_
: 把虚拟DOM转换成真实DOM$mount
:挂载方法
(3). src/core/index.js
- 与平台无关
- 设置了Vue的静态方法,initGlobalAPl(Vue)
(4). src/core/instance/index.js
- 与平台无关
- 定义了构造函数,调用了this.init(options) 方法
- 给Vue中混入了常用的实例成员
2. 看源码中的两个问题
(1) 关闭JS语法检查爆红问题
在vscode的settings.json里,将"javascript.validate.enable"设为false
(2) flow中的泛型影响代码的高亮显示
在vscode插件商城里安装Babel JavaScript这个插件,可以让泛型后面的代码可以高亮显示,但失去了跳转链接的作用
3. 静态方法
./src/core/global-api/index.js
// 设置keep-alive组件
extend(Vue.options.components, builtInComponents)
// 注册Vue.use()用来注册组件