1. Vue.extend
1.1 用法回顾
其用法如下:
Vue.extend( options )
-
参数:
{Object} options
-
作用:
使用基础
Vue
构造器,创建一个“子类”。参数是一个包含组件选项的对象。data
选项是特例,需要注意 - 在Vue.extend()
中它必须是函数<div id="mount-point"></div>
// 创建构造器 var Profile = Vue.extend({ template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>', data: function () { return { firstName: 'Walter', lastName: 'White', alias: 'Heisenberg' } } }) // 创建 Profile 实例,并挂载到一个元素上。 new Profile().$mount('##mount-point')
结果如下:
<p>Walter White aka Heisenberg</p>
1.2 原理分析
通过用法回顾我们知道,Vue.extend
的作用是创建一个继承自Vue
类的子类,可接收的参数是一个包含组件选项的对象。
既然是Vue
类的子类,那么除了它本身独有的一些属性方法,还有一些是从Vue
类中继承而来,所以创建子类的过程其实就是一边给子类上添加上独有的属性,一边将父类的公共属性复制到子类上。接下来,我们就来看看源码是如何实现这个过程的。
该API的定义位于源码的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
}
可以看到,虽然代码量稍微有点多,但是逻辑并不复杂,下面我们就来逐行分析一下。
首先,该函数内部定义了几个变量,如下:
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
- extendOptions:用户传入的一个包含组件选项的对象参数;
- Super:指向父类,即基础
Vue
类; - SuperId:父类的
cid
属性,无论是基础Vue
类还是从基础Vue
类继承而来的类,都有一个cid
属性,作为该类的唯一标识; - cachedCtors:缓存池,用于缓存创建出来的类;
接着,在缓存池中先尝试获取是否之前已经创建过的该子类,如果之前创建过,则直接返回之前创建的。之所以有这一步,是因为Vue
为了性能考虑,反复调用Vue.extend
其实应该返回同一个结果,只要返回结果是固定的,就可以将结果缓存,再次调用时,只需从缓存中取出结果即可。在API方法定义的最后,当创建完子类后,会使用父类的cid
作为key
,创建好的子类作为value
,存入缓存池cachedCtors
中。如下:
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
接着,获取到传入的选项参数中的name
字段,并且在开发环境下校验name
字段是否合法,如下:
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
validateComponentName(name)
}
接着,创建一个类Sub
,这个类就是将要继承基础Vue
类的子类,如下:
const Sub = function VueComponent (options) {
this._init(options)
}
到这里,我们已经把类创建好了,接下来的工作就是让该类去继承基础Vue
类,让其具备一些基础Vue
类的能力。
首先,将父类的原型继承到子类中,并且为子类添加唯一标识cid
,如下:
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
接着,将父类的options
与子类的options
进行合并,将合并结果赋给子类的options
属性,如下:
Sub.options = mergeOptions(
Super.options,
extendOptions
)
接着,将父类保存到子类的super
属性中,以确保在子类中能够拿到父类,如下:
Sub['super'] = Super
接着,如果选项中存在props
属性,则初始化它,如下:
if (Sub.options.props) {
initProps(Sub)
}
function initProps (Comp) {
const props = Comp.options.props
for (const key in props) {
proxy(Comp.prototype, `_props`, key)
}
}
初始化props
属性其实就是把参数中传入的props
选项代理到原型的_props
中。
接着,如果选项中存在computed
属性,则初始化它,如下:
if (Sub.options.computed) {
initComputed(Sub)
}
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
函数是一样的。
接着,将父类中的一些属性复制到子类中,如下:
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]
// 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)
最后,使用父类的cid
作为key
,创建好的子类Sub
作为value
,存入缓存池cachedCtors
中。如下:
// cache constructor
cachedCtors[SuperId] = Sub
最终将创建好的子类Sub
返回。文章来源:https://www.toymoban.com/news/detail-839571.html
以上,就是Vue.extend
的所有逻辑。其实总体来讲,整个过程就是先创建一个类Sub
,接着通过原型继承的方式将该类继承基础Vue
类,然后给Sub
类添加一些属性以及将父类的某些属性复制到Sub
类上,最后将Sub
类返回。文章来源地址https://www.toymoban.com/news/detail-839571.html
到了这里,关于Vue源码系列讲解——全局API篇【一】(Vue.extend)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!