-
Notifications
You must be signed in to change notification settings - Fork 0
Description
在上一篇构造函数部分,我们知道了在 global-api 添加全局 API 的时候,在 Vue 上增加了 Vue.extend 方法,因为考虑到后面讲解 initMixin 部分有涉及到这部分的内容,所以今天先来讲讲这个 Vue.extend 好了。
通过 Vue 的 extend 文档 我们知道,它是使用基础 Vue 构造器,可以用来创建一个“子类”。比如:
<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'
}
}
})
new Profile().$mount('#mount-point')结果如下:
<p>Walter White aka Heisenberg</p>我们直接来看下代码:
export function initExtend (Vue: GlobalAPI) {
/**
* Each instance constructor, including Vue, has a unique
* cid. This enables us to create wrapped "child
* constructors" for prototypal inheritance and cache them.
*/
Vue.cid = 0
let cid = 1
/**
* Class inheritance
*/
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
}
}下面我们来逐行分析下代码,extendOptions 为通过 Vue.extend 透传进去的 options 对象,Super 指向当前构造函数 Vue。初始给 Vue 添加一个 cid,它的值为0,之后每次通过 Vue.extend 创建的子类的 cid 值依次递增。extendOptions._Ctor 是用来缓存子类构造函数,如果已存在就直接返回。
在讲全局 API 的时候,我们有讲到过 Vue.component 这个方法,它是用来注册子组件的
// src/core/global-api/assets.js
export function initAssetRegisters(Vue: GlobalAPI) {
ASSET_TYPES.forEach(type => {
Vue[type] = function(
id: string,
definition: Function | Object
): Function | Object | void {
...
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id
definition = this.options._base.extend(definition)
}
}
})
}如果当 type = 'component' 且 definition 是一个对象时,比如:
Vue.component('my-component-name', { })就会执行 this.options._base.extend(definition),相当于是 Vue.extend(definition),这里的 name 就是 my-components-name。所以在 Vue.extend 中下面这段代码是用来校验组件名称是否规范用的。
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
validateComponentName(name)
}再下面就是继承部分了,创建 Sub 构造函数,同时把父类的原型赋值到 Sub.prototype 上。除了原型链的继承,还把父类的静态属性和方法也一并继承了下来:
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最终返回子类。
小结
我们可以看下,现在子类 Sub 上已经包含了哪些属性和方法:
Sub.cid
Sub.options // 包含了父类的 options 和子类本身的 options
Sub.extend
Sub.mixin
Sub.use
Sub.component
Sub.directive
Sub.filter
// 新增
Sub.super = Super
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)对比了上一篇中父类上列举的属性和方法,除了新增部分,Sub 上没有的属性包括:
Vue.config
Vue.util
Vue.set
Vue.delete
Vue.nextTick
Vue.observable