This is the second day of my participation in the August More Text Challenge
preface
Mixins are the last thing I mentioned when I looked at parameter merging. Vue. Extend () uses the same merge strategy that applies to mixins. Today we’ll look at a concrete implementation of extend:
Vue.extend
Vue.extend is a global API defined in SRC /core/global-api/extend.js
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
}
}
Copy the code
Vue.extend constructs a ‘subclass’ of Vue that takes an object containing component options as an argument. So let’s see what this code actually does. Okay
SuperId
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
Copy the code
As you can see from the code, the SuperId primarily works with cachedCtors to serve the cache. Here this is the caller of the extend method, which is Vue.
Take a look at cid:
/** * 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
Copy the code
You can see from the comments above that each instance, including Vue, has a unique CID to identify itself. So the SuperId is unique.
It also checks if the passed extendOptions object has a _Ctor attribute, and if it does not, it assigns an empty object to _Ctor.
validateComponentName
Next we process the component name, which is used if the extension object passed in the name field, otherwise we get it from the Options object of the Super class.
const name = extendOptions.name || Super.options.name
Copy the code
To determine if the component name is valid, we call the validateComponetName method, defined in SRC /core/util/options.js
export function validateComponentName (name: string) {
if (!new RegExp(`^[a-zA-Z][\-\.0-9_${unicodeRegExp.source}] * $`).test(name)) {
warn(
'Invalid component name: "' + name + '". Component names ' +
'should conform to valid custom element name in html5 specification.')}if (isBuiltInTag(name) || config.isReservedTag(name)) {
warn(
'Do not use built-in or reserved HTML elements as component ' +
'id: ' + name
)
}
}
Copy the code
SRC /core/util/lang.js: SRC /core/util/lang.js: SRC /core/util/lang.
Sub
Here’s the focus of the extend function, which converts an object into a constructor that extends from Vue via stereotype inheritance. As you can see, every time Sub is instantiated, _init goes through Vue initialization logic.
const Sub = function VueComponent (options) {
this._init(options)
}
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub['super'] = Super
Copy the code
The Sub constructor uniquely flags CID by 1, and then calls mergeOptions to merge the parameters.
Sub.cid = cid++
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Copy the code
After merging the parameters, initialize props and computed
if (Sub.options.props) {
initProps(Sub)
}
if (Sub.options.computed) {
initComputed(Sub)
}
Copy the code
Copy the extend, mixin, use, and hook methods of the parent class to the subclass
// allow further extension/mixin/plugin usage
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub
}
Copy the code
Save the options parameters of the parent class and the component object itself, as well as the extendOptions object originally passed in
// 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)
Copy the code
Caches the Sub object into the _Ctor object with the cid key of the parent class
// cache constructor
cachedCtors[SuperId] = Sub
return Sub
Copy the code