前篇
上面在主流程篇讲到了Vue函数在声明和扩展上的一些逻辑,本篇主要从Vue的初始化来讲解,来看看New Vue()到底干了什么事情。
主入口初始化
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
// 告警信息打印
warn('Vue is a constructor and should be called with the `new` keyword')
}
// 初始化
this._init(options)
}
上面代码就是Vue函数进行调用的主入口函数,做了两件事情,先判断了是不是New来进行初始化的,然后调用了_init方法,_init方法是在主流程篇initMixin的时候挂载到了Vue的原型对象上面。
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// 赋值,_uid,也就是虚拟dom的key
vm._uid = uid++
// 赋值,标识为Vue
vm._isVue = true
// 判断调用,_isComponent未Vue内部属性,初始化的时候会走到下面
if (options && options._isComponent) {
initInternalComponent(vm, options)
} else {
// 使用合并策略,对options进行合并,并赋值给$options
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
// 代理
vm._renderProxy = vm
// 代理
vm._self = vm
// 初始化生命周期,也就是简单的赋值
initLifecycle(vm)
// 初始化事件处理机制
initEvents(vm)
// 初始化渲染函数,给$createElement等赋值
initRender(vm)
// 调用生命周期钩子函数beforeCreate
callHook(vm, 'beforeCreate')
// 初始化inject,父子通信相关
initInjections(vm) // resolve injections before data/props
// 划重点,data、props、watch、computed、methods等全部初始化完毕
initState(vm)
// 初始化provide,父子通信相关
initProvide(vm) // resolve provide after data/props
// 调用生命周期钩子函数,created
callHook(vm, 'created')
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
上面代码部分,首先会对传进来的options进行合并处理,mergeOptions函数接下来会讲,会对其合并规则进行说明。
- initLifecycle会对当前Vue实例初始化一些属性:
- initEvents会对当前实例的事件进行处理,调用主流程篇eventsMixin添加上的off、emit方法进行事件的添加:
- initRender会对当前实例添加与渲染相关的函数,以便render的时候进行使用:
- 接下来就调用了Vue的生命周期函数-beforeCreate
- initInjections会对新增的inject进行处理,此处会关闭后面提到的Observe:
- initState会对传递进来的data、props、watch、computed、methods进行init处理,具体细节代码解读会在接下来的文章进行讲解,看下图:
- initProvide,初始化_provide属性,并对_provide进行赋值
- 调用Vue的生命周期函数-created,前面只是对实例添加属性和方法,并没有进行构建DOM,连虚拟DOM也没有创建,所以在created构造函数里面无法操作dom
- 判断options是否有el属性,如有则进行mount
合并规则
mergeOptions函数是对数据进行合并,接受三个参数,第一个参数为Vue构造函数上在声明篇讲到的options;第二个参数为传递进来的options,第三个参数为当前实例。
第一个参数:
第二个参数,就是下面{}里面的内容;
var vm = new Vue({
el: '#demo',
data() {
return {
message: 'hello vue',
name: 'Draven'
}
}
})
第三个参数为当前实例,即vm。接下来咱们看下mergeOptions的流程
export function mergeOptions (parent: Object, child: Object, vm?: Component): Object {
// 如果是子组件,则获取子组件的options
if (typeof child === 'function') {
child = child.options
}
// 处理child的Props,把my_prop_name改为myPropName
normalizeProps(child, vm)
// 处理child的inject,对inject进行封装,标记from
normalizeInject(child, vm)
// 处理child的directives,如为函数,则设置为bind和update时调用
normalizeDirectives(child)
// 如果不是最顶层的child,最顶层的parent
if (!child._base) {
// 对extends进行递归调用合并
if (child.extends) {
parent = mergeOptions(parent, child.extends, vm)
}
// 对mixins进行递归调用合并
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
}
// 根据合并策略strats合并options
const options = {}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
咱们来看看strats里面都有哪些合并的策略,然后咱们一一来分析:
上图就是目前版本所有的合并策略,看起来挺多,其实就分为几类,一一来看下。
合并规则-mergeHook
所有钩子函数的合并都是调用的mergeHook,钩子函数包括如下几种:
- 'beforeCreate',
- 'created',
- 'beforeMount',
- 'mounted',
- 'beforeUpdate',
- 'updated',
- 'beforeDestroy',
- 'destroyed',
- 'activated',
- 'deactivated',
- 'errorCaptured',
- 'serverPrefetch' 来看下mergeHook部分的代码:
function mergeHook (
parentVal: ?Array<Function>,
childVal: ?Function | ?Array<Function>
): ?Array<Function> {
const res = childVal
? parentVal
? parentVal.concat(childVal)
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal
return res
? dedupeHooks(res)
: res
}
function dedupeHooks (hooks) {
const res = []
for (let i = 0; i < hooks.length; i++) {
if (res.indexOf(hooks[i]) === -1) {
res.push(hooks[i])
}
}
return res
}
LIFECYCLE_HOOKS.forEach(hook => {
strats[hook] = mergeHook
})
LIFECYCLE_HOOKS是个数组,包含的就是上面列举的目前版本包含的钩子函数; mergeHook函数写的是真的骚,这么多问号,TMD!!!拿其中一个钩子函数(created,父组件的created和子组件的created)来举例,先看第一个三元表达式:
- 如果无子created,则res为父created;
- 如有子created,则检测父created,如果已经有父created,则在父created后面拼接上子created
- 如有子created,无父created,则直接取子created,并把子created序列化为数组
整理如下图
现在获取到了当前要使用的res,对res进行去重操作(dedupeHooks),然后返回对应的hooks,处理结束。
合并规则-mergeAssets
使用mergeAssets规则进行合并的有components、directives、filters,共计三种。 来看看mergeAssets部分的代码:
function mergeAssets (
parentVal: ?Object,
childVal: ?Object,
vm?: Component,
key: string
): Object {
const res = Object.create(parentVal || null)
if (childVal) {
return extend(res, childVal)
} else {
return res
}
}
ASSET_TYPES.forEach(function (type) {
strats[type + 's'] = mergeAssets
})
上面先是用parentVal创建了个新对象,然后判断childVal:如果有,则进行合并操作,合并到刚才创建的对象,然后返回;如果无,则直接进行返回;
流程图如下:
合并规则-mergeWatch
使用mergeWatch合并规则的就只有watch,来看看mergeWatch部分的代码,
function mergeWatch(
parentVal: ?Object,
childVal: ?Object,
vm?: Component,
key: string
): ?Object {
if (parentVal === nativeWatch) parentVal = undefined
if (childVal === nativeWatch) childVal = undefined
if (!childVal) return Object.create(parentVal || null)
if (!parentVal) return childVal
const ret = {}
extend(ret, parentVal)
for (const key in childVal) {
let parent = ret[key]
const child = childVal[key]
if (parent && !Array.isArray(parent)) {
parent = [parent]
}
ret[key] = parent
? parent.concat(child)
: Array.isArray(child) ? child : [child]
}
return ret
}
上面部分代码,先是对parentVal和childVal进行了判断和赋值,然后检测了childVal;
如无childVal,返回一个新的parentVal对象;如无parentVal,返回childVal;都有则开始继承,遍历childVal,往ret里面赋值,此处所赋的值都为数组,流程图如下:
合并规则-mergeDataOrFn
使用mergeDataOrFn的合并规则的属性有data和provide两个,其中data会先进行一部分处理再调用mergeDataOrFn,先看下这部分代码:
function mergeData(
parentVal: any,
childVal: any,
vm?: Component
): ?Function {
if (!vm) {
if (childVal && typeof childVal !== 'function') {
return parentVal
}
return mergeDataOrFn(parentVal, childVal)
}
return mergeDataOrFn(parentVal, childVal, vm)
}
对传递进来的数据进行了讨论,如果无实例对象,并且childVal有值,同时childVal不是一个函数,则直接返回parentVal. 接下来看mergeDataOrFn的代码:
function mergeDataOrFn (
parentVal: any,
childVal: any,
vm?: Component
): ?Function {
if (!vm) {
if (!childVal) {
return parentVal
}
if (!parentVal) {
return childVal
}
return function mergedDataFn () {
return mergeData(
typeof childVal === 'function' ? childVal.call(this, this) : childVal,
typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal
)
}
} else {
return function mergedInstanceDataFn () {
// instance merge
const instanceData = typeof childVal === 'function'
? childVal.call(vm, vm)
: childVal
const defaultData = typeof parentVal === 'function'
? parentVal.call(vm, vm)
: parentVal
if (instanceData) {
return mergeData(instanceData, defaultData)
} else {
return defaultData
}
}
}
}
上面代码先是对实例进行了判断,分为两个分支,一个是无实例的话:无childVal返回parentVal,无parentVal返回childVal,都有的话,返回一个mergedDataFn函数,另外一个分支则是有实例的话,直接返回一个mergedInstanceDataFn函数。
上面返回的两个函数内部都是调用的mergeData,咱们可以直接去看mergeData函数的实现。
function mergeData (to: Object, from: ?Object): Object {
if (!from) return to
let key, toVal, fromVal
const keys = hasSymbol ? Reflect.ownKeys(from) : Object.keys(from)
for (let i = 0; i < keys.length; i++) {
key = keys[i]
if (key === '__ob__') continue
toVal = to[key]
fromVal = from[key]
if (!hasOwn(to, key)) {
set(to, key, fromVal)
} else if (toVal !== fromVal && isPlainObject(toVal) && isPlainObject(fromVal)) {
mergeData(toVal, fromVal)
}
}
return to
}
此处的merge是相当于把两个对象进行合并,返回要合并到的对象,不是一个新建的对象,简单画了下流程图
合并规则-mergeOther
起了个简单的名字-mergeOther,使用到此规则的属性包含props、methods、inject、computed四个属性。
function mergeOther(parentVal: ?Object, childVal: ?Object, vm?: Component, key: string): ?Object {
if (!parentVal) return childVal
const ret = Object.create(null)
extend(ret, parentVal)
if (childVal) extend(ret, childVal)
return ret
}
简单merge,继承市合并,返回新对象
合并规则-defaultStrat
这个更加简单,只是简单的判断返回,仅一行代码
function defaultStrat(parentVal: any, childVal: any): any {
return childVal === undefined ? parentVal : childVal
}
至此,7个merge策略已经完成,下面是整体的流程图
整体流程图
这就是new Vue所做的事情,后面我们会梳理细节。