Vue2.x源码解读-组件化

组件的概念
   Vue组件就是拥有预定义选项的Vue实例。

组件的注册方式

  1. 全局组件:通过Vue.component方法注册。
  2. 局部组件:在组件的选项参数的components中定义,只能在注册的组件中使用。

Vue.component方法
  Vue.component方法是在Vue初始化静态方法时和directivefilter一起定义的。Vue.component内部的主要实现是将用户传入的选项参数通过Vue.extend方法转换成Vue子类的构造函数,并将其保存到全局的组件中。

ASSET_TYPES.forEach(type => {
  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)
      }
      // Vue.component('comp', { template: '' })
      if (type === 'component' && isPlainObject(definition)) {
        definition.name = definition.name || id
        // 把组件配置转换为组件的构造函数 this.options._base = Vue
        definition = this.options._base.extend(definition)
      }
      if (type === 'directive' && typeof definition === 'function') {
        definition = { bind: definition, update: definition }
      }
      // 全局注册,存储资源并赋值
      // this.options['components']['comp'] = definition
      this.options[type + 's'][id] = definition
      return definition
    }
  }
})

组件的创建过程
  在Vue实例化时,会逐步调用_init --> $mount --> mountComponent --> new Watcher --> updateComponent --> _render --> createElement --> createComponent 。最终调用createComponent创建组件对应的VNodecreateComponent执行过程中会将组件的hooks复制到选项参数的data中,包括initprepatchinsertdestroy

组件的渲染过程
  在创建完组件的VNode之后,会将VNode传递给_update函数,然后在__patch__函数中将VNode转化为真实DOM,并挂载到视图上。调用_update函数时(注意这里调用_update方法的是父组件)会调用setActiveInstance改变activeInstance的存储的内容,并将上一个activeInstance的值保存到prevActiveInstance,并返回一个能够将activeInstance重置会prevActiveInstance的函数,此时activeInstance保存的是父组件的实例。
  __patch__函数中会先调用createElm,然后在createElm中再调用createComponent来创建组件对应的真实DOM
  在createComponent中调用创建组件VNode时传入的init hook函数来完成组件真实DOM 的创建和挂载。
  init函数中会调用createComponentInstanceForVnode来创建组件实例,同时将activeInstance传入createComponentInstanceForVnode方法。activeInstance此时存放的是父组件的实例。
  createComponentInstanceForVnode方法中则是通过new子组件的构造函数来创建组件实例的。在子组件的构造函数中将调用_.init方法。这里和Vue的实例化过程是一样的。

const Sub = function VueComponent (options) {
  // 调用 _init() 初始化
   this._init(options)
 }

  _.init方法执行时会调用initLifecycle,记录父子组件的关系,在这里将会将子组件的实例放到父组件实例的$children属性中,将父组件实例记录在子组件的$parent属性中。
  createComponentInstanceForVnode方法成功返回之后并不会去调用$mount挂载子组件,只是把子组件实例保存在vnode.componentInstance中,然后继续向下执行createComponent,在createComponent中挂载子组件。

function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
  let i = vnode.data
  if (isDef(i)) {
    const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
    if (isDef(i = i.hook) && isDef(i = i.init)) {
      // 调用 init() 方法,创建和挂载组件实例
      // init() 的过程中创建好了组件的真实 DOM,挂载到了 vnode.elm 上
      i(vnode, false /* hydrating */)
    }
    // after calling the init hook, if the vnode is a child component
    // it should've created a child instance and mounted it. the child
    // component also has set the placeholder vnode's elm.
    // in that case we can just return the element and be done.
    if (isDef(vnode.componentInstance)) {
      // 调用钩子函数(VNode的钩子函数初始化属性/事件/样式等,组件的钩子函数)
      initComponent(vnode, insertedVnodeQueue)
      // 把组件对应的 DOM 插入到父元素中
      insert(parentElm, vnode.elm, refElm)
      if (isTrue(isReactivated)) {
        reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
      }
      return true
    }
  }
}

两个重要结论

  1. 组件创建时会先创建父组件再创建子组件。
  2. 组件挂载时会先挂载子组件再挂载父组件。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值