Vue Implementation 02
Static Properties and Functions in Vue (Global API)
Till now, we went through core/instance/index.js
. Now we are looking into core/index.js
. This file introduced Vue
from core/instance/index.js
. We open core/index.js
here is the code:
// import Vue from its born place
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
// pass Vue constructor as parameter, and pass it into initGlobalAPI. This is imported by ./global-api/index.js
initGlobalAPI(Vue)
// add $isServer property to Vue.prototype. This property proxies isServerRendering in core/util/env.js
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
// add $ssrContext property to Vue.prototype
Object.defineProperty(Vue.prototype, '$ssrContext', {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})
// Vue.version stores the current version of Vue
Vue.version = '__VERSION__'
// export Vue
export default Vue
In code above, it imported Vue
in instance/index.js
, and imported three variables from three different files:
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
initGlobalAPI
is a funcion, and use Vue
as a parameter of constuctor.
initGlobalAPI(Vue)
Then two more readonly properties is added to Vue.prototype
, which are $isServer
and $ssrContent
. Then it defined FunctionalRenderContext
as a static property, and it is imported from FunctionalRenderContext
in core/vdom/create-functional-component/js
. These properties are designed to use in Server-side rendering
.
Lastly. it added version
to Vue.constructor
and it storages the current version of Vue
. But where do the __VERSION__
come from? Open scripts/config.js
and find genConfig
. There is a line: __VERSION__: version
. This line is written in replace
plugin in rollup
. That is __VERSION__
will be replaced by version
finally and version
is the version of Vue
.
Let's get back to this line:
initGlobalAPI(Vue)
We can assume that this added some global API into Vue
. In fact, these global API is attached to Vue
's constructor as static properties and functions. Open src/core/global-api/index.js
and look what initGlobalAPI
do.
Firstly, a piece of code:
// config
const configDef = {}
configDef.get = () => config
if (process.env.NODE_ENV !== 'production') {
configDef.set = () => {
warn(
'Do not replace the Vue.config object, set individual fields instead.'
)
}
}
Object.defineProperty(Vue, 'config', configDef)
It defines config
property to Vue.constructor
. The apporach to add this property is similar to how to add $data
and $props
before, and it is readonly as well. When you try to set its value directly, you wiill get a friendly warning if you are not in production.
What is Vue.config
? In src/core/global-api/index.js
, we can find:
import config from '../config'
So what Vue.config
proxying is the object exported in core/config.js
.
And it is followed by this code:
// exposed util methods.
// NOTE: these are not considered part of the public API - avoid relying on
// them unless you are aware of the risk.
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
It defines util
property to Vue
, and it is an object. It contains four properties which are warn
, extend
, mergeOptions
and defineReactive
. They are imported by core/util/index.js
.
There is a piece of comment that means Vue.util
and the four functions in util
is not defined as public API and we should avoid depending on these APIs. However, you can invoke them at your own risk. Besides, in official documentation, these APIs are not introduced as well, so we just use them as less as possible.
And it is followed by this code:
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
Vue.options = Object.create(null)
This part is quite simple. It defined four properties which is set
, delete
, nextTick
and options
. We should note that Vue.options
which is an empty object right now, and it is created by Object.create(null)
.
But then, Vue.options
is not an empty object anymore because of the following code:
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)
In code above, ASSER_TYPES
is imported by shared/constants.js
. Open this file, we can know ASSET_TYPES
is an array:
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]
So after execution of this code:
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
Vue.option
become something like this:
Vue.options = {
components: Object.create(null),
directives: Object.create(null),
filters: Object.create(null),
_base: Vue
}
And it is followed by this line:
extend(Vue.options.components, builtInComponents)
extend
is imported by shared/util.js
. We can refer to //TODO link to find its effect. At all, this code is mixing properties from builtInComponents
to Vue.options.components
. builtInComponents
is imported by core/components/index.js
, coded as below:
import KeepAlive from './keep-alive'
export default {
KeepAlive
}
Therefore, Vue.options.components
became something like this:
Vue.options.components = {
KeepAlive
}
Till now, Vue.options
became:
Vue.options = {
components: {
KeepAlive
},
directives: Object.create(null),
filters: Object.create(null),
_base: Vue
}
Let's dive into code then. In the last part of initGlobalAPI
, it invoked four init*
functions by passing Vue
as parameter.
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
They are imported by global-api/use.js
, global-api/mixin.js
, global-api/extend.js
and global-api/assets.js
. Let's make them clear one by one. Open global-api/use.js
and we found only a function called initUse
:
/* @flow */
import { toArray } from '../util/index'
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
// ...
}
}
It defines use
on the constructor of Vue
, which also known as the global API called Vue.use
. We are quite familiar with it that it is used to install plugins to Vue
.
Open global-api/mixin.js
as well, and we found it is simpler:
/* @flow */
import { mergeOptions } from '../util/index'
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}
initMixin
is used to add mixin
to Vue
as a global API.
Open global-api/extend.js
and look into initExtend
:
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 {
// ...
}
}
initExtend
added a static property called Vue.cid
to Vue
. Then, it defined Vue.extend
to Vue
.
The last one is initAssetRegisters
. Open global-api/assets.js
and find initAssetRegisters
:
export function initAssetRegisters (Vue: GlobalAPI) {
/**
* Create asset registration methods.
*/
ASSET_TYPES.forEach(type => {
Vue[type] = function (
id: string,
definition: Function | Object
): Function | Object | void {
// ......
}
})
}
ASSET_TYPES
which is included in shared/constants.js
is what we once met before. So after initAssetRegisters
, Vue
have three more static functions:
Vue.component
Vue.directive
Vue.filter
These three static API is used to add globally registered component, directives and filter.
Therefore, we introduced initGlobalAPI
which act as it named. It added three global API to Vue.constructor
.
Now, we are clear about what core/index.js
do.
- In this file, it imported a core
Vue
(a.k.a.Vue
fromcore/instance/index.js
, which is decorated by multiple properties and functions). - Then, it uses
initGlobalAPI
to add static functions and properties toVue
. - Besides, it modified prototype and defined two properties called
$isServer
and$ssrContext
. - Finally, it defined
Vue.version
and exportedVue
.