Vue源码学习之初始化模块init.js解析

前端之家收集整理的这篇文章主要介绍了Vue源码学习之初始化模块init.js解析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

我们看到了VUE分了很多模块(initMixin()stateMixin()eventsMixin()lifecycleMixin()renderMixin()),通过使用Mixin模式,都是使用了JavaScript原型继承的原理,在Vue的原型上面增加属性方法。我们继续跟着this._init(options)走,这个一点击进去就知道了是进入了init.js文件是在initMixin函数里面给Vue原型添加的_init方法。首先来从宏观看看这个init文件,可以看出主要是导出了两个函数:initMixin和resolveConstructorOptions,具体作用我们一步步来讨论。咋的一看这个文件,可能有些童鞋会看不明白函数参数括号里面写的是什么鬼,这个其实是应用了flow的类型检查,具体flow的使用这里就不介绍了,有兴趣的请移步:https://flow.org/en/

我们现在来看第一个函数initMixin,Vue实例在初始化的时候就调用了这个函数

export function initMixin (Vue: Class) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// a uid
vm._uid = uid++

let startTag,endTag
/ istanbul ignore if / 【注:istanbul 是代码覆盖率检测工具,此注释为代码测试用
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = vue-perf-init:${vm._uid}
endTag = vue-perf-end:${vm._uid}
mark(startTag)
}

// a flag to avoid this being observed
vm._isVue = true
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow,and none of the
// internal component options needs special treatment.
initInternalComponent(vm,options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),options || {},vm
)
}
/ istanbul ignore else /
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm,'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm,'created')

/ istanbul ignore if /
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm,false)
mark(endTag)
measure(${vm._name} init,startTag,endTag)
}

if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}

我们本着宏观简化原则,这个函数里面前面有三个if判断工作我们可以先不细化讨论,大致第一个是用performance做性能监测,第二个合并option,第三个是做代理拦截,是ES6新特性,可参考阮一峰大神关于proxy的介绍【http://es6.ruanyifeng.com/#docs/proxy】。那么就进入了初始化函数主要点:

if (vm.$options.el) {
vm.$mount(vm.$options.el)
}

这里来一个插曲start

V2.1.8及以前的版本】这里比较方便理解在生命周期created之后再做render,那么在created之前就无法获取DOM。这也是在有些源码解析文章里面很容易见到的分析,也是正确的

v2.1.9及以后的版本】但到这里一开始就懵逼了很久render提到beforeCreate之前去了,那岂不是DOM在beforeCreate之前就能获取到了?显然不对了,请注意render虽然提前了,但是后面多了一个if这个if里面才获取DOM的关键,这个if在2.1.8版本之前是在render函数里面的,在2.1.9之后被提出来,然后render函数提前了,至于为何提前暂未了解,此处只是踩了一个看其他源码解析不同版本带来的坑!

插曲end,继续

1.initLifecycle

// locate first non-abstract parent
let parent = options.parent //我理解为父实例或者父组件
if (parent && !options.abstract) { //例子中没有parent,断点代码的时候自动跳过
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}

vm.$parent = parent
vm.$root = parent ? parent.$root : vm

vm.$children = []
vm.$refs = {}

vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
}

这个函数主要是有父实例的情况下处理vm.$parent和vm.$children这俩个实例属性,我此处没有就跳过,其他的就是新增了一些实例属性

2.initEvents

又新增两个属性,后面那个if条件里面是有父组件的事件时初始化,估计就是props和events父子组件通信的事件内容

3.initRender

createElement(vm,a,d,false) vm.$createElement = (a,true) const parentData = parentVnode && parentVnode.data /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { defineReactive(vm,'$attrs',parentData && parentData.attrs,() => { !isUpdatingChildComponent && warn(`$attrs is readonly.`,vm) },true) defineReactive(vm,'$listeners',vm.$options._parentListeners,() => { !isUpdatingChildComponent && warn(`$listeners is readonly.`,true) } else { defineReactive(vm,null,true) } }

函数也是初始化了节点属性信息,绑定createElement函数到实例【并未挂载】,接下来调用beforeCreate回调钩子;——TODO1:后续专题分析VUE渲染逻辑

4.initInjections

{ /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { defineReactive(vm,key,result[key],() => { warn( `Avoid mutating an injected value directly since the changes will be ` + `overwritten whenever the provided component re-renders. ` + `injection being mutated: "${key}"`,vm ) }) } else { defineReactive(vm,result[key]) } }) observerState.shouldConvert = true } }

函数也是当有inject属性时做处理,源码例子无inject断点跑暂时跳过

5.initState

可以看出此处是对options传入的props/methods/data/computed/watch属性做初始化————TODO2:分析每个属性的初始化

6.initProvide

这个函数跟4.initInjections在同一个inject.js中,也是在传入参数有provide属性时做处理,暂时跳过,然后就到了created回调钩子,最后的vm.$mount接入TODO1;

今天initMixin到此结束,以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

猜你在找的Vue相关文章