vue源码学习之组件化

组件化

例子

Vue.component('Com1', { template: '<div>component</div>' })

全局定义components方法

src/core/global-api/index.js

//ASSET_TYPES 包含了component,directive,filter
Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null) // 在vue的options中添加了components,filters,directives
  })

就可以在组件中通过设置components:{componentName }引入其他组件,filters跟directive类似。

组件声明

Vue.component()
src/core/global-api/assets.js

initAssetRegisters()
export function initAssetRegisters (Vue: GlobalAPI) { // 声明组件(可以是指令,组件,过滤器)
  ASSET_TYPES.forEach(type => { 
    // 传进来vue, Vue.component()
    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)) {
          definition.name = definition.name || id
          // 组件构造函数创建VueComponent  _base就是Vue
          definition = this.options._base.extend(definition)
        }
        if (type === 'directive' && typeof definition === 'function') {
          definition = { bind: definition, update: definition }
        }
        // 注册组件 components:{}
        this.options[type + 's'][id] = definition
        return definition
      }
    }
  })
}

创建根组件

首先创建的是根组件,首次_render()时,会得到整棵树的VNode结构。
_createElement()
src/core/vdom/create-element.js

else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) { 
  vnode = createComponent(Ctor, data, context, children, tag) // 创建组件
}

由于传入tag是非保留标签,因此判定为自定义组件通过createComponent()去创建。

createElement()
创建组件VNode,保存了上一步处理得到的组件构造函数,props,事件等。

createComponent()

src/core/vdom/create-component.js

export function createComponent(){
...
// 安装组件钩子函数
  installComponentHooks(data) // 合并用户设置的和默认的生命周期
...
  const name = Ctor.options.name || tag
  // 自定义组件的vnode
  const vnode = new VNode(
    `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
    data, undefined, undefined, undefined, context,
    { Ctor, propsData, listeners, tag, children },
    asyncFactory
  )
  ...
  return vnode; // 最终返回vnode
}

installComponentHooks中有一个componentVNodeHooks()
src/core/vdom/create-component.js

const componentVNodeHooks = {
	init(){} // 组件实例化及挂载
}

组件创建

在创建根组件的时候会递归向下创建这些子组件。
src/core/vdom/patch.js
第一次创建根组件的时候采用的是这个函数。

	createElm(
          vnode,
          insertedVnodeQueue,
          // extremely rare edge case: do not insert if old element is in a
          // leaving transition. Only happens when combining transition +
          // keep-alive + HOCs. (#4590)
          oldElm._leaveCb ? null : parentElm,
          nodeOps.nextSibling(oldElm)
        )

在根组件创建好后就会创建子组件
createElm()

function createElm (){
  ...
  // 尝试创建组件实例
    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
      return
    }
    ...
}

createComponent()

function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) { // 自定义组件创建
    let i = vnode.data
    if (isDef(i)) {
      const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
      // 如果存在init钩子函数就执行(保留标签不存在init函数,就可以去除掉保留标签,只创建自定义的组件)
      // 执行实例创建和挂载
      // 渲染自上而下,挂载自上而下,先挂载在父元素上,等父元素中的所有子组件都挂载完在执行渲染
      if (isDef(i = i.hook) && isDef(i = i.init)) {
        i(vnode, false /* hydrating */)
      }
      // 插入到父元素中,元素属性创建
      if (isDef(vnode.componentInstance)) {
        initComponent(vnode, insertedVnodeQueue)
        insert(parentElm, vnode.elm, refElm)
        if (isTrue(isReactivated)) {
          reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
        }
        return true
      }
    }
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章