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
      }
    }
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章