概述
Vue支持封装HTML元素为组件,组件还可以组装成另一个组件,提高可复用性。
那么,有没有想过这些是怎么办到的呢?
简单地拆分一小块HTML代码?不是的,前面提到过,Vue是通过render函数生成DOM的,所以组件也应该是由一个JavaScript对象表示,并且该对象会包含一个render函数。
其实除了根Vue实例,其它的组件都是Vue的子类的实例。
构造Vue子类的方法
Vue的子类是通过API:Vue.extend()
来生成的:
Vue.extend = function (extendOptions: Object): Function {
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
validateComponentName(name)
}
const Sub = function VueComponent (options) {
this._init(options)
}
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
if (Sub.options.props) {
initProps(Sub)
}
if (Sub.options.computed) {
initComputed(Sub)
}
// allow further extension/mixin/plugin usage
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// create asset registers, so extended classes
// can have their private assets too.
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// cache constructor
cachedCtors[SuperId] = Sub
return Sub
}
复制代码
在使用vue-cli开发的使用,每一个.vue文件会被解析转化为一个普通对象,该对象的render属性是通过template转化来的。然后这个普通对象会被传入Vue.extend()
,生成一个Vue的子类。
也就是说,从根Vue类开始,components属性会连接很多Vue子类,然后Vue子类的components也会连接其它的Vue子类。它们会在适当的时机里实例化并挂载到父组件。
组件的实例化过程
在根Vue实例或者组件的render函数执行过程中,如果遇到另一个组件,就会使用内部的createComponent
处理,大概分为三个步骤:
- 使用
Vue.extend()
创建Vue子类 - 把组件的钩子函数(包括init、prepatch、insert、destroy)注册到snabbdom的patch过程中
- 返回组件vnode(children属性为undefined)
在根Vue实例或者组件的patch函数执行过程中,调用组件的初始化钩子函数——内部会调用createComponentInstanceForVnode
返回组件实例,并在恰当时机挂载组件到父组件。
组件的生命周期
与根Vue实例一样,组件也是有创建-挂载-更新三个阶段。
且各个组件(包括父子组件、兄弟组件)的更新都是独立的,也就是说,一个组件的options.data
中的数据更新并触发组件render重新执行的时候,并不会导致其它的组件更新。