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