开始
接着上文写,上文讲到了initproxy代理,接下去讲一些初始化的东西
// 暴露自己
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(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
复制代码
我们重要的函数一个个讲过来
initLifecycleinitEvents
初始化生命周期的
export function initLifecycle (vm: Component) {
const options = vm.$options
// locate first non-abstract parent
let parent = options.parent
if (parent && !options.abstract) {
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
}
复制代码
initEvents
初始化事件
export function initEvents (vm: Component) {
//挂载一个event空对象
vm._events = Object.create(null)
vm._hasHookEvent = false
// 存在监听器的话,就更新组件的监听器
const listeners = vm.$options._parentListeners
if (listeners) {
updateComponentListeners(vm, listeners)
}
}
复制代码
initRender
渲染用的,之后render的实现会讲到
export function initRender (vm: Component) {
vm._vnode = null // the root of the child tree
vm._staticTrees = null // v-once cached trees
const options = vm.$options
const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
const renderContext = parentVnode && parentVnode.context
vm.$slots = resolveSlots(options._renderChildren, renderContext)
vm.$scopedSlots = emptyObject
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
// 最主要的记住这个
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
const parentData = parentVnode && parentVnode.data
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
!isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
}, true)
defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
!isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
}, true)
} else {
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
}
}
复制代码
callHook 调用
export function callHook (vm: Component, hook: string) {
// #7573 disable dep collection when invoking lifecycle hooks
pushTarget()
//handler是undefined
const handlers = vm.$options[hook]
const info = `${hook} hook` //beforeCreate hook
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
invokeWithErrorHandling(handlers[i], vm, null, vm, info)
}
}
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook)
}
popTarget()
}
你在Vue实例化的时候挂载一个钩子,就可以调用了,这挂载了beforeCreate,就会被hook调用到
var s=new Vue({
render: h => h(App),
beforeCreate(){
console.log(123);
}
}).$mount('#app')
复制代码
initState
这个是进行数据绑定的,里面的initData很重要
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
//undefined
if (opts.props) initProps(vm, opts.props)
//undefined
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
//执行之后,可以用this直接访问data的数据 this.a
initData(vm)
} else {
//主要是这里 observe
observe(vm._data = {}, true /* asRootData */)
}
// 下面2个都没执行
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
复制代码
initData
function initData (vm: Component) {
let data = vm.$options.data
//返回实例化传入的data的return的对象
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
if (!isPlainObject(data)) {
data = {}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
//props,data,method的属性不能一样,因为最终都会挂到vm上,会冲突
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${key}" has already been defined as a data property.`,
vm
)
}
}
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
//a 是options里面data的一个参数,如下
// new Vue({
// el: '#app',
// router,
// data:{
// a:"1"
// },
// components: { App },
// template: '<App/>'
// })
//
//这里下面执行完就是this.a = this._data.a == 上面的data.a
proxy(vm, `_data`, key)
}
}
// 这个是观察者模式的,先不看,要和watcher,dep一起理解
observe(data, true /* asRootData */)
}
复制代码
上面比较重要proxy,proxy就是做了一层代理,这样我们能在比如mounted()这个生命周期,直接this.a访问data里面的a。
proxy
直接看代码
export function proxy (target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter () {
//这里就是代理过去 this['_data'][key] ,相当于访问this.a=vm['_data'][a] vm['_data']上面initData就初始化为data了
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
复制代码
总结
上面写了initMixin之后执行的一些函数,主要是initData,initRender 要注意下,一个会有个代理过程,另一个后面写render的时候会用到。