研究组件的注册
//组件注册
// ASSET_TYPES = ['component','filter','directive'] ASSET_TYPES.forEach(type => { // 声明静态的方法: Vue.component = function(){} // 平时声明的组件是Vue.component('comp',{}) //id ='comp' ,definition = {} 配置 Vue[type] = function ( id: string, definition: Function | Object ): Function | Object | void { if (!definition) { return this.options[type + 's'][id] } else { /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && type === 'component') { validateComponentName(id) } if (type === 'component' && isPlainObject(definition)) { // name的设置 definition.name = definition.name || id // this.options._base指的是vue的构造函数,实际上是Vue.extend({}),Vue.extend({})返回是是Ctor组件的构造函数 // 当用Vue.extend({})组件的构造函数,最后可以用new Ctor()去实例化组件的构造函数 // 将传入的组件配置对象转换为组件构造函数 definition = this.options._base.extend(definition) } if (type === 'directive' && typeof definition === 'function') { definition = { bind: definition, update: definition } } // 向全局的选项中加入全局组件配置对象 // 每个组件上components的id [id]添加构造函数 Ctor,即 // components[id] = Ctor // Vue.component
// 到时候mergeOptions()的时候再把this.options merge进去,这样就变成了全局组件,其他的全局组件还包括keep-alive,transition等 this.options[type + 's'][id] = definition return definition } } }) }
全局组件注册完了,接下来生命周期就开始了,怎么去实例化Ctor,平时大家看的时候,只看过怎么去new Vue的,从来没看过怎么去实例化组件的。
new Vue的时候,首次会执行render(),由于在执行根实例的render的时候,可以得到整棵树的虚拟dom的结构
所以接下来从initRender入手 srccoreinstance ender.js
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 // bind the createElement fn to this instance // so that we get proper render context inside it. // args order: tag, data, children, normalizationType, alwaysNormalize // internal version is used by render functions compiled from templates // 由编译器生成的渲染函数调用_c vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false) // normalization is always applied for the public version, used in // user-written render functions. // 其实是render函数中的参数h vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true) // $attrs & $listeners are exposed for easier HOC creation. // they need to be reactive so that HOCs using them are always updated 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 { // 定义$attrs和$listeners响应式 defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true) defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true) } }
接下来看createElement srccorevdomcreate-element.js
createElement的作用是将传入的组件配置转化为vnode
// 核心逻辑(生成虚拟dom) let vnode, ns if (typeof tag === 'string') { // 拿到组件的构造函数 Ctor let Ctor ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag) // 判断是否是保留标签 div p span if (config.isReservedTag(tag)) { // platform built-in elements if (process.env.NODE_ENV !== 'production' && isDef(data) && isDef(data.nativeOn)) { warn( `The .native modifier for v-on is only valid on components but it was used on <${tag}>.`, context ) } vnode = new VNode( config.parsePlatformTagName(tag), data, children, undefined, undefined, context ) // isDef(Ctor)有没有定义一个Ctor构造函数 } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) { // component // Ctor 这里就是组件构造函数 得到vnode
这里就用到了createComponent
vnode = createComponent(Ctor, data, context, children, tag) } else { // unknown or unlisted namespaced elements // check at runtime because it may get assigned a namespace when its // parent normalizes children vnode = new VNode( tag, data, children, undefined, undefined, context ) } } else { // direct component options / constructor vnode = createComponent(tag, data, context, children) }
进入到createComponent里面
// 安装组件的管理钩子 // 管理钩子:合并用户编写的钩子喝系统默认的钩子 installComponentHooks(data) => hooksToMerge就是系统默认的钩子 hooksToMerge = Object.keys(componentVNodeHooks)
// return a placeholder vnode
// 把组件构造函数的名称name拿出来,如果没有,就用tag(它自己的名称)
const name = Ctor.options.name || tag
// vue-component-1-comp
const vnode = new VNode(
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
data, undefined, undefined, undefined, context,
{ Ctor, propsData, listeners, tag, children },
asyncFactory
)
接下来具体看componentVNodeHooks
最后组件实例要进行挂载
父子组件生命周期 创建的时候自上而下,挂载的时候自下而上(洋葱模型)
上面的init是什么时候执行的?
答案:会在patch的时候执行
parent.$mount => render()方法 => update()函数 => _patch_() => _patch_方法中有createElement()方法,init就是在这个时候被调用的