组件的概念
Vue组件就是拥有预定义选项的Vue实例。
组件的注册方式
- 全局组件:通过
Vue.component方法注册。 - 局部组件:在组件的选项参数的
components中定义,只能在注册的组件中使用。
Vue.component方法
Vue.component方法是在Vue初始化静态方法时和directive、filter一起定义的。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创建组件对应的VNode。createComponent执行过程中会将组件的hooks复制到选项参数的data中,包括init、prepatch、insert和destroy。
组件的渲染过程
在创建完组件的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
}
}
}
两个重要结论
- 组件创建时会先创建父组件再创建子组件。
- 组件挂载时会先挂载子组件再挂载父组件。
本文详细解析了Vue组件的创建与渲染过程,包括组件的概念、注册方式、创建与挂载流程等核心内容。
475

被折叠的 条评论
为什么被折叠?



