組件化
例子
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
}
}
}