前言
最近想要对 Vue2 源码进行学习,主要目的就是为了后面在学习 Vue3 源码时,可以有一个更好的对比和理解,所以这个系列暂时不会涉及到 Vue3 的内容,但是 Vue3 的核心模块和 Vue2 是一致的,只是在实现上改变了方式、进行了优化等。
准备工作
再开始阅读源码之前,有些事还是必须要做的,那就是拉取 源仓库代码 或者直接下载 ZIP
格式文件,处理好之后就需要打开神器 VScode
打开编辑器之后,你需要做的就是:
- 安装依赖
install
—— yarn 或者 npm - 找到
package.json
文件并找到里面的scripts
部分,然后往dev
命令中加入--sourcemap
配置参数,或者新建建一个dev:sourcemap
命令,其内容就是比dev
命令多了个--sourcemap
配置,其实主要就是为了生成vue.js.map
文件方便后面调试 - 执行
npm run dev:sourcemap
命令,执行成功以后就会在dist
目录下生成vue.js.map
文件
- 然后就可以在
example
目录下,写一些自己想要测试的例子,然后通过debug
的形式找到对应内容在代码中的位置
深入源码
Vue 初始化的代码位置
既然要深入了解 Vue 初始化的内容,那么我们就得先找到 Vue 初始化是在哪里进行的,那么查找的方式有两种:
- 通过 package.json 文件来进行查找 —— 在 script 脚本命令配置中有
"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev"
,其中的scripts/config.js
和TARGET:web-full-dev
就为指明了文件里的具体配置,然后可以在逐层查找对应的入口文件 - 通过 debug 模式进行查找 —— 首先在
example
目录下新建一个目录,可以是任何名字,本文讨论的是初始化的内容,这里就将其命名为init
,在init
目录下新建一个html
文件,在里面引入dist
目录下的vue.js
, 因为生成的map
文件是vue.js.map
,否则debug
时不方便查找对应的文件位置
这里选择方式二,毕竟方式一通过 rollup
配置查找还是过于繁琐,于是通过 debug
可以快速确定 Vue
进行初始化的文件位置.
this._init(options) 初始化
在 src>core>index.js
文件中,可以清晰的看到,在我们进行 new Vue()
时,调用的其实就是 this._init()
方法,而这个方法又是在 initMixin(Vue)
中进行定义的
initMixin(Vue) 方法
在 src>core>init.js
文件中,可以看到在 initMixin()
方法中最核心的就是处理组件配置项的部分,这一部分又分为 子组件 和 根组件 的配置,又分别对应 initInternalComponent()
方法 和 mergeOptions()
方法 + resolveConstructorOptions()
方法.
同时,在 Vue.prototype._init
函数中还存在下面的这些方法的调用,后面会依次对每个方法进行解读:
子组件 —— initInternalComponent() 方法
这个方法做的事就是创建 $options
对象,然后对组件选项进行打平做性能优化,因为组件有很多的配置,其中也会存在各种嵌套的配置,在访问时免不了要通过原型链进行动态查找,会影响执行效率.
- 根据
vm
的构造函数创建新的配置对象,即平时访问的$options
对象 - 把当前组件配置项打平然后赋值到
$options
对象,避免了原型链的动态查找 - 如果当前组件配置项中存在
render
选项,就把它添加到$options
对象上
根组件 —— resolveConstructorOptions() 方法
在 src>core>init.js
文件中,resolveConstructorOptions()
方法其实最主要的事情就是从构造函数上解析配置对象,具体如下:
- 如果构造函数的
super
属性存在,证明还有基类,此时需要递归进行对配置选项解析 - 将构造函数的基类配置项进行缓存,然后比对当前配置项配置项进行对比,如果不一致,则表明基类的配置项已发生更改
- 找出被更改的配置项和
extent
选项进行合并,并赋值给$options
根组件 —— mergeOptions() 方法
在 src>core>options.js
文件中,mergeOptions()
方法主要做的事情就是:
- 对配置选项进行标准化
- 对传入的原始配置对象进行合并
- 返回新的配置对象 根组件合并配置项,就是将全局配置项合并到根组件局部配置项中,比如将全局注册的
Vue.componet(options)
全局配置合并到根组件new Vue(options)
上得到类似:
new Vue({ el: xxx, data: { xxx }, componets:{ localComponents, globalComponents, } }) 复制代码
initLifecycle() 方法
在 src>core>init.js
文件中调用 initLifecycle()
方法,方法的具体定义位置为src>core>lifecycle.js
.
可能很多人会误以为该方法是初始化生命周期钩子函数的(因为其方法名),其实这个方法主要是对组件关系属性进行初始化,比如:$root、$parent、$children、$refs
等.