How Vue.js Resolves Constructor Options: A Deep Dive into initMixin and resolveConstructorOptions
This article explains how Vue.js parses and merges constructor options during instance creation, covering the initMixin flow, the resolveConstructorOptions function for both base Vue constructors and Vue.extend subclasses, and the underlying source files that define core components, directives, filters, and the _base reference.
Preface
In the previous article we analyzed Vue's overall runtime mechanism and saw that Vue calls the init() function during initialization.
In instance.js the initMixin() method is invoked, which leads to the following code in init.js:
// merge options
if (options && options._isComponent) {
// if the current Vue instance is a component, execute initInternalComponent
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the internal component options needs special treatment.
initInternalComponent(vm, options) // this method mainly adds some properties to vm.$options
} else {
// otherwise instantiate a Vue object and call mergeOptions
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}This code merges options; the question is how the options are actually merged.
When directly instantiating Vue (i.e., new Vue({})) the following call is made:
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)The mergeOptions function receives three parameters:
Parameter 1: resolveConstructorOptions(vm.constructor) Parameter 2: options || {} (the options passed when creating the Vue instance)
Parameter 3: vm (the Vue instance itself)
Thus the key is the first parameter, the resolveConstructorOptions function.
resolveConstructorOptions
This function parses the options on the current instance constructor.
/**
* @param {*} Ctor: vm.constructor
* This method handles two cases:
* 1. Ctor is the base Vue constructor.
* 2. Ctor is a subclass created via Vue.extend.
*/
export function resolveConstructorOptions (Ctor: Class<Component>) {
let options = Ctor.options
// If Ctor has a super property, it means Ctor is a subclass created by Vue.extend
if (Ctor.super) {
const superOptions = resolveConstructorOptions(Ctor.super)
const cachedSuperOptions = Ctor.superOptions // Vue constructor's options
if (superOptions !== cachedSuperOptions) {
// super option changed, need to resolve new options.
Ctor.superOptions = superOptions
// check if there are any late-modified/attached options (#4976)
const modifiedOptions = resolveModifiedOptions(Ctor)
// update base extend options
if (modifiedOptions) {
extend(Ctor.extendOptions, modifiedOptions)
}
options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
if (options.name) {
options.components[options.name] = Ctor
}
}
}
return options
}new Vue() Constructor Subclass
Base Vue Constructor Case (new Vue())
export function resolveConstructorOptions (Ctor: Class<Component>) {
let options = Ctor.options
// If Ctor has a super property, it means Ctor is a subclass created by Vue.extend
...
return options
}Here Ctor is the Vue constructor, and Ctor.options is the options object on the Vue constructor, which is returned directly.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>new Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">{{message}}</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
created: function (){
console.log('this.constructor.options ', this.constructor.options)
}
})
</script>
</body>
</html>Printing vm.constructor.options shows the four base properties: components, directives, filters, and _base. These are defined during framework initialization.
Looking at package.json we find the dev script:
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev"In scripts/config.js the target web-full-dev points to the entry file:
'web-full-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.js'),
format: 'umd',
env: 'development',
alias: { he: './entity-decoder' },
banner
}The entry file imports Vue from ./runtime/index:
import Vue from './runtime/index'
...
import platformDirectives from './directives/index'
import platformComponents from './components/index'
// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)Platform directives export the global directives v-model and v-show, while platform components export the global animation components Transition and TransitionGroup.
Further inspection of global-api/index.js reveals how Vue.options is built:
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
...
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// this is used to identify the "base" constructor to extend all plain-object components with in Weex's multi-instance scenarios.
Vue.options._base = Vue
extend(Vue.options.components, builtInComponents)Thus the four base properties ( components, directives, filters, _base) are defined here.
Vue.extend Constructor Subclass
Examining Ctor.super (Vue.extend)
// Vue.extend function adds a super property to the subclass
Vue.extend = function (extendOptions: Object): Function {
...
Sub['super'] = Super
...
}
/**
* @param {*} Ctor: Ctor.super
* This method handles two cases:
* 1. Ctor is the base Vue constructor.
* 2. Ctor is a subclass created via Vue.extend.
*/
export function resolveConstructorOptions (Ctor: Class<Component>) {
let options = Ctor.options // equivalent to vm.constructor.options
// If Ctor has a super property, it means Ctor is a subclass created by Vue.extend
if (Ctor.super) { // equivalent to vm.constructor.super
const superOptions = resolveConstructorOptions(Ctor.super)
const cachedSuperOptions = Ctor.superOptions // Vue constructor's options
if (superOptions !== cachedSuperOptions) {
// super option changed, need to resolve new options.
Ctor.superOptions = superOptions
// check if there are any late-modified/attached options (#4976)
const modifiedOptions = resolveModifiedOptions(Ctor)
// update base extend options
if (modifiedOptions) {
extend(Ctor.extendOptions, modifiedOptions)
}
options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
if (options.name) {
options.components[options.name] = Ctor
}
}
}
return options
}Example usage of Vue.extend:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>new Vue</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app2"></div>
<script>
var Profile = Vue.extend({
template: '<p>123</p>'
})
console.log('new profile super ', new Profile().constructor.super)
console.log('new profile superOptions', new Profile().constructor.superOptions)
console.log('new profile constructor ', new Profile().constructor)
console.log('new profile constructor options', new Profile().constructor.options)
new Profile().$mount('#app2')
</script>
</body>
</html>The initExtend function defines Vue.extend and creates the subclass:
export function initExtend (Vue: GlobalAPI) {
...
Vue.extend = function (extendOptions: Object): Function {
extendOptions = extendOptions || {}
const Super = this
const Sub = function VueComponent (options) {
this._init(options) // calls Vue's _init
}
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super
...
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
cachedCtors[SuperId] = Sub
return Sub
}
}Thus the subclass inherits the base constructor, adds its own options (merged from Super.options and extendOptions), and stores references to super, superOptions, extendOptions, and a sealed copy of its options. options: merged result of Super.options and
extendOptions super: points to the base Vue constructor superOptions: the base constructor's options at extension time extendOptions: the user‑provided options passed to
Vue.extend sealedOptions: a frozen copy of Sub.options Printing the subclass constructor properties shows that vm.constructor.super is the Vue constructor, vm.constructor.superOptions contains the inherited base options, and vm.constructor.options includes the four base properties plus any custom properties such as template.
if (Ctor.super) {
const superOptions = resolveConstructorOptions(Ctor.super)
const cachedSuperOptions = Ctor.superOptions // Vue constructor's options
if (superOptions !== cachedSuperOptions) {
// super option changed, need to resolve new options. Replace its own options with the latest
Ctor.superOptions = superOptions
const modifiedOptions = resolveModifiedOptions(Ctor)
if (modifiedOptions) {
extend(Ctor.extendOptions, modifiedOptions)
}
options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
if (options.name) {
options.components[options.name] = Ctor
}
}
}In summary, resolveConstructorOptions parses the options on the current instance constructor, handling both the base Vue constructor and subclasses created via Vue.extend. Understanding this flow requires familiarity with ES5 prototype inheritance.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
WeDoctor Frontend Technology
Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
