vue2源码解析(三)

研究组件的注册
//组件注册
// 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就是在这个时候被调用的

原文地址:https://www.cnblogs.com/gengzhen/p/15440205.html