前言
上篇,借助生命周期图简单介绍了整个 Vue 的声明过程,这篇开始正式进入 Vue.prototype._init 方法,以其为起始点,看整个 Vue 的内部代码逻辑。
初始化 _init()
先看下整个初始化的代码(因为排版,略作删减,有需要还是 clone 整个项目查看,后续不再做说明):
1 | Vue.prototype._init = function(options?: Object) { |
属性合并 mergeOptions
我们定义 Vue 对象的参数,会被 _init 方法接收,并作为 options 选项对象参数:
1 | { |
1 | Vue.prototype._init = function(options?: Object) {}; |
之后会判断 options._isComponent ,当然现在肯定为 false ,随后程序会调用 mergeOptions 方法,进行合并选项参数的工作:
1 | vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor), options || {}, vm); |
Constructor 解析
如果你愿意可以看下,它是怎么对 this 上的 Constructor 引用做解析的:
1 | export function resolveConstructorOptions(Ctor: Class<Component>) { |
最后结果就是通过递归该方法,把 vm.constructor 上的父类引用 Ctor.super 的属性和当前对象引用 Ctor 的属性做继承,合并成一个符合要求的 options 返回。
不过有些其他的概念可以说下,我们知道一个构造函数声明后,会有个 prototype 原型对象,并且原型对象有个 constructor 指针指回构造函数,代码如下:
1 | function Parent(options) { |
normalize 标准化选项
解析完 Constructor 后,就进入 mergeOptions 方法了,然后迎来针对 props、inject、directives 的 标准化解析:
1 | export function mergeOptions(parent: Object, child: Object, vm?: Component): Object { |
因为 Vue 为方便我们的“各种”方式的使用,做了多样的 api,最后都要通过标准化的转化让 vue 内部来正确使用:
下面是这三个属性的转化说明:
props
1 | props: Array<string> | Object |
1 | function normalizeProps(options: Object, vm: ?Component) { |
inject
1 | inject: Array<string> | { [key: string]: string | Symbol | Object } |
1 | function normalizeInject(options: Object, vm: ?Component) { |
directives
1 | directives: Object |
1 | function normalizeDirectives(options: Object) { |
mergeField 字段合并
当标准化后,会分别遍历 parent 和 child 的属性字段,根据特定字段的 合并策略 进行 merge 操作。
1 | const options = {}; |
合并策略 strat
这里简单说下,vue 默认会对重要的选项属性有个合并策略。
1 | // src\core\util\options.js |
默认策略 defaultStrat
属性:el、propsData
以最简单的方式来合并字段,如果 child 不存在就用 parent
1 | const defaultStrat = function(parentVal: any, childVal: any): any { |
特殊定义
属性:lifecycle
根据预设的生命周期数组,挨个遍历初始化钩子 hook 的策略
1 | const LIFECYCLE_HOOKS = ["beforeCreate", "created", "beforeMount", "mounted", "beforeUpdate", "updated", "beforeDestroy", "destroyed", "activated", "deactivated", "errorCaptured", "serverPrefetch"]; |
1 | LIFECYCLE_HOOKS.forEach(hook => { |
属性:component、directive、filter 策略:
如上三个 api 设置 s 命名,定义合并策略
1 | ASSET_TYPES.forEach(function(type) { |
继承策略
属性:props、methods、inject、computed
以 child 优先,覆盖 parent 属性值
1 | function (parentVal,childVal,vm,key): ?Object { |
数据合并方法策略 mergeDataOrFn
属性:data、provide
如果参数是 function 类型,会通过 call 来做预执行操作,将结果作为 parent 和 child 的合并前提。
1 | export function mergeDataOrFn(parentVal: any, childVal: any, vm?: Component): ?Function { |
数据合并策略 mergeData
用于 mergeDataOrFn 策略中的基础方案
会根据遍历 to、from 各自对象上的属性,互相作对比,以 to 优先
1 | function mergeData(to: Object, from: ?Object): Object { |
所有属性遍历完后,得到最终的 options 交付给 vm.\$options 。