学习vue不得不学习vue实例上得方法,extend是一个组件继承vue实例得函数,我们就来看看Vue.extend函数得实现原理是什么?
用法:
Vue.extend( options )
参数:
{Object} options
作用:
使用基础 Vue
构造器,创建一个“子类”。参数是一个包含组件选项的对象。
data
选项是特例,需要注意 - 在 Vue.extend()
中它必须是函数。
// 创建构造器
var Profile = Vue.extend({
template: '{{firstName}} {{lastName}} aka {{alias}}
',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('##mount-point')
最后得结果是这样得:
Walter White aka Heisenberg
原理:src/core/global-api/extend.js
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
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
}
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// cache constructor
cachedCtors[SuperId] = Sub
return Sub
}
以上是源码,从源码中可以看到,在最开始,声明了四个变量:
Vue
类;cid
属性,无论是基础 Vue
类还是从基础 Vue
类继承而来的类,都有一个cid
属性,作为该类的唯一标识;接着,在缓存池中先尝试获取之前已经创建过的该子类,如果之前创建过,则直接返回之前创建的。之所以有这一步,是因为Vue
为了性能考虑,反复调用Vue.extend
其实应该返回同一个结果,只要返回结果是固定的,就可以将结果缓存,再次调用时,只需从缓存中取出结果即可。在API方法定义的最后,当创建完子类后,会使用父类的cid
作为key
,创建好的子类作为value
,存入缓存池cachedCtors
中。
紧接着,从传入的组件选项参数对象中获取name字段,并且如果是生成环境,就去校验这个name是否合法。
然后创建了一个Sub的类,用来继承Vue基础类的子类。所以类里面执行了init函数,初始化事件,state,等(声明周期中的init函数)。
所以,现在创建好了子类,并且继承了vue基础类的属性。具备了vue类的功能。
那么接下来就是将父类的原型继承到子类中,并且为子类添加上唯一的cid标识。然后将父类的options和子类的options进行合并,将结果赋给子类的options。紧接着将父类保存到子类的super中,这样保证能在子类中获取到父类。
接下来是对props的处理,如果在options中存在props,那么就初始化props,如果options中存在computed属性,那么同样初始化computed。
function initProps (Comp) {
const props = Comp.options.props
for (const key in props) {
proxy(Comp.prototype, `_props`, key)
}
}
function initComputed (Comp) {
const computed = Comp.options.computed
for (const key in computed) {
defineComputed(Comp.prototype, key, computed[key])
}
}
初始化props
属性就是遍历参数中传入的computed
选项,将每一项都调用defineComputed
函数定义到子类原型上。此处的defineComputed
函数与我们之前在生命周期初始化阶段initState
中所介绍的defineComputed
函数是一样的。
然后将父类复制到子类中去。给子类新增三个属性。最后,使用父类的cid
作为key
,创建好的子类Sub
作为value
,存入缓存池cachedCtors
中。
最终将创建好的子类Sub
返回。
以上,就是Vue.extend
的所有逻辑。其实总体来讲,整个过程就是先创建一个类Sub
,接着通过原型继承的方式将该类继承基础Vue
类,然后给Sub
类添加一些属性以及将父类的某些属性复制到Sub
类上,最后将Sub
类返回。