The target
Learn more about how the following global apis are implemented.
-
Vue.use
-
Vue.mixin
-
Vue.component
-
Vue.filter
-
Vue.directive
-
VUe.extend
-
Vue.set
-
Vue.delete
-
Vue.nextTick
The source code interpretation
As you can see from the introduction to the source code directory structure in the first article in the series, Vue Source Code Interpretation (1), most of Vue’s global API implementations are in the/SRC /core/global-api directory. The source reading entry for these global apis is in the/SRC /core/global-api/index.js file.
The entrance
/src/core/global-api/index.js
/** * initialize Vue's global apis, such as: * Default: Vue. Vue.util. Xx * Vue.set, vue.delete, vue.nexttick, vue.Observable * Vue.options.components, vue.options. directives, vue.options. Directives, vue.options. Filters, vue.options._base * Use, vue.extend, vue.mixin, Vue.component, vue.directive, vue.filter * */
export function initGlobalAPI (Vue: GlobalAPI) {
// config
const configDef = {}
// Many default configurations for Vue
configDef.get = () = > config
if(process.env.NODE_ENV ! = ='production') {
configDef.set = () = > {
warn(
'Do not replace the Vue.config object, set individual fields instead.')}}// Vue.config
Object.defineProperty(Vue, 'config', configDef)
/** * Expose some tools, do not use them easily, you know the tools, and know the risks */
Vue.util = {
// Warning log
warn,
// Similar options merge
extend,
// Merge options
mergeOptions,
// Set the response
defineReactive
}
// Vue.set / delete / nextTick
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
// The response method
Vue.observable = <T>(obj: T): T= > {
observe(obj)
return obj
}
// Vue.options.compoents/directives/filter
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type= > {
Vue.options[type + 's'] = Object.create(null)})// Mount the Vue constructor to vue.options._base
Vue.options._base = Vue
// Add built-in components to the Vue.options.components, such as keep-alive
extend(Vue.options.components, builtInComponents)
// Vue.use
initUse(Vue)
// Vue.mixin
initMixin(Vue)
// Vue.extend
initExtend(Vue)
// Vue.component/directive/filter
initAssetRegisters(Vue)
}
Copy the code
Vue.use
/src/core/global-api/use.js
/** * defines Vue. Use, which is responsible for installing the plug-in for Vue. * 1@param {*} The plugin install method or object * containing the install method@returns Vue instance * /
Vue.use = function (plugin: Function | Object) {
// The list of plugins that have been installed
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
// Check whether the plugin is already installed
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// Put the Vue constructor in the first argument position and pass those arguments to the install method
const args = toArray(arguments.1)
args.unshift(this)
if (typeof plugin.install === 'function') {
// If plugin is an object, execute its install method to install the plug-in
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
// Install the plugin using the direct plugin method
plugin.apply(null, args)
}
// Add the newly installed plug-in to the plugin list
installedPlugins.push(plugin)
return this
}
Copy the code
Vue.mixin
/src/core/global-api/mixin.js
/** * defines Vue. Mixin, which is responsible for the global mixin option, and affects all Vue instances created later, which will incorporate the global mixin option *@param {*} Mixin Vue configuration object *@returns Return Vue instance */
Vue.mixin = function (mixin: Object) {
// Merge mixin objects on Vue's default configuration
this.options = mergeOptions(this.options, mixin)
return this
}
Copy the code
mergeOptions
src/core/util/options.js
/** * Merge two options. When the same configuration item appears, the child option overwrites the configuration of the parent option */
export function mergeOptions (
parent: Object,
child: Object, vm? : Component) :Object {
if(process.env.NODE_ENV ! = ='production') {
checkComponents(child)
}
if (typeof child === 'function') {
child = child.options
}
// standardise props, inject, and directive options
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
// Handles extends and mixins on the original Child object, executing mergeOptions, respectively, to merge these inherited options into the parent
// mergeOptions handles objects that have the _base attribute
if(! child._base) {if (child.extends) {
parent = mergeOptions(parent, child.extends, vm)
}
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
}
const options = {}
let key
// Iterate over the parent option
for (key in parent) {
mergeField(key)
}
If the parent option does not have the configuration, merge it, otherwise skip it, because the case of the parent having the same property was already handled when the parent option was processed above, using the value of the child option
for (key in child) {
if(! hasOwn(parent, key)) { mergeField(key) } }// Merge options, childVal takes precedence over parentVal
function mergeField (key) {
// If strat is a merge policy function, childVal overwrites parentVal
const strat = strats[key] || defaultStrat
// If childVal exists, childVal is preferred; otherwise, parentVal is used
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
Copy the code
Vue.component, vue.filter, vue.directive
/src/core/global-api/assets.js
These three API implementations are special, but the principles are similar, so they are implemented together.
const ASSET_TYPES = ['component'.'directive'.'filter']
/** * Define the Vue.component, vue.filter, and vue.directive methods * The three methods do similar things, For example, Vue.component(compName, {xx}) as a result, this.options.components.com pName = component constructor * ASSET_TYPES = [' component ', 'directive', 'filter'] * /
ASSET_TYPES.forEach(type= > {
/** * For example: Vue.component(name, definition) *@param {*} id name
* @param {*} Definition Component constructor or configuration object *@returns Returns the component constructor */
Vue[type] = function (
id: string,
definition: Function | Object
) :Function | Object | void {
if(! definition) {return this.options[type + 's'][id]
} else {
if (type === 'component' && isPlainObject(definition)) {
// If there is a name in the component configuration, use it; otherwise, use the ID
definition.name = definition.name || id
Extend = vue.extend; // extend = vue.extend; // extend = vue.extend; // extend = vue.extend; // extend = vue.extend;
definition = this.options._base.extend(definition)
}
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition }
}
// this.options.components[id] = definition
// Incorporate globally registered components into the components of each component's configuration object via mergeOptions at instantiation time
this.options[type + 's'][id] = definition
return definition
}
}
})
Copy the code
Vue.extend
/src/core/global-api/extend.js
/** * 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
/** * extend the subclass based on Vue, which also supports further extensions * extension can pass some default configurations, just like Vue has some default configurations * default configurations are merged if they conflict with the base class */
Vue.extend = function (extendOptions: Object) :Function {
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
/** * Use the cache, and return the constructor in the cache if it exists * When can the cache be used? * This cache is enabled if you use the same configuration item (extendOptions) for multiple calls to vue.extend */
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)
}
// Define the Sub constructor, just like the Vue constructor
const Sub = function VueComponent(options) {
/ / initialization
this._init(options)
}
// Inherit Vue by prototype inheritance
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
// Option merge, merge Vue configuration items into their own configuration items
Sub.options = mergeOptions(
Super.options,
extendOptions
)
// Keep track of your base class
Sub['super'] = Super
// Initialize the props configuration proxy to sub.prototype. _props
// It is accessible within the component using this._props
if (Sub.options.props) {
initProps(Sub)
}
// Initialize computed and delegate the computed configuration to the sub.prototype object
// The component can be accessed using this.computedKey
if (Sub.options.computed) {
initComputed(Sub)
}
// Define three static methods: extend, mixin, and use, which allow subclasses to be constructed on top of Sub
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// Define static methods for Component, filter, and directive
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// Recursive component principle. If the component has the name attribute set, it registers itself with its components options
if (name) {
Sub.options.components[name] = Sub
}
// Keep references to base class options when extending.
// Later, when instantiating, we can check whether the Super option has been updated
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
/ / cache
cachedCtors[SuperId] = Sub
return Sub
}
function initProps (Comp) {
const props = Comp.options.props
for (const key in props) {
proxy(Comp.prototype, `_props`, key)
}
}
function initComputed (Comp) {
const computed = Comp.options.computed
for (const key in computed) {
defineComputed(Comp.prototype, key, computed[key])
}
}
Copy the code
Vue.set
/src/core/global-api/index.js
Vue.set = set
Copy the code
set
/src/core/observer/index.js
$set val * If target is an object and the key does not already exist, then set a response for the new key and perform dependency notification */
export function set (target: Array<any> | Object, key: any, val: any) :any {
if(process.env.NODE_ENV ! = ='production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)}Vue. Set (array, idx, val)
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
Vue.set(obj, key, val)
if (key intarget && ! (keyin Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
// Cannot add dynamic add response attribute to Vue instance or $data, one of the uses of vmCount,
// this.$data ob.vmCount = 1, indicating the root component, other subcomponents vm
if(target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV ! = ='production' && warn(
'Avoid adding reactive properties to a Vue instance or its root $data ' +
'at runtime - declare it upfront in the data option.'
)
return val
}
// Target is not a reactive object. New properties are set, but not reactive
if(! ob) { target[key] = valreturn val
}
// Define a new attribute for the object, set the response via defineReactive, and trigger the dependency update
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
Copy the code
Vue.delete
/src/core/global-api/index.js
Vue.delete = del
Copy the code
del
/src/core/observer/index.js
/** * deletes the target object's array of specified keys * via the splice method. The object deletes the specified key * via the delete operator and performs the dependency notification */
export function del (target: Array<any> | Object, key: any) {
if(process.env.NODE_ENV ! = ='production' &&
(isUndef(target) || isPrimitive(target))
) {
warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)}// If target is an array, the splice method deletes the element with the specified index
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.splice(key, 1)
return
}
const ob = (target: any).__ob__
// Avoid deleting Vue instance attributes or $data
if(target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV ! = ='production' && warn(
'Avoid deleting properties on a Vue instance or its root $data ' +
'- just set it to null.'
)
return
}
// If the attribute does not exist, end directly
if(! hasOwn(target, key)) {return
}
// Delete the properties of an object with the delete operator
delete target[key]
if(! ob) {return
}
// Perform dependency notifications
ob.dep.notify()
}
Copy the code
Vue.nextTick
/src/core/global-api/index.js
Vue.nextTick = nextTick
Copy the code
nextTick
/src/core/util/next-tick.js
For a more detailed analysis of the nextTick method, see Vue source code Interpretation (4) — Asynchronous Update in the previous article.
const callbacks = []
/** * Do two things: * 1, use the try catch flushSchedulerQueue packaging function, and then put it in the callbacks array * 2, if the pending to false, * If pending is true, flushCallbacks are already in the browser's task queue. * If pending is true, flushCallbacks are already in the browser's task queue. Pending is set to false again, indicating that the next flushCallbacks can enter the browser's task queue@param {*} Cb Receives a callback function => flushSchedulerQueue *@param {*} CTX context *@returns * /
export function nextTick (cb? :Function, ctx? :Object) {
let _resolve
// Use the callbacks array to store the wrapped CB function
callbacks.push(() = > {
if (cb) {
// Wrap the callback function with a try catch to catch errors
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')}}else if (_resolve) {
_resolve(ctx)
}
})
if(! pending) { pending =true
// Execute timerFunc and put the flushCallbacks function in the browser's task queue (the preferred microtask queue)
timerFunc()
}
// $flow-disable-line
if(! cb &&typeof Promise! = ='undefined') {
return new Promise(resolve= > {
_resolve = resolve
})
}
}
Copy the code
conclusion
-
Interviewer: What did Vue.use(plugin) do?
A:
To install a plugin, perform the install method provided by the plugin.
-
First determine if the plug-in has already been installed
-
If no, install the plug-in using the install method provided by the plug-in. The plug-in decides what to do
-
-
Interviewer: What did Vue.mixin(Options) do?
A:
Is responsible for merging options configurations on Vue’s global configuration. The global configuration is then merged into the component’s own configuration when each component generates a VNode.
-
Standardizes the formatting of props, inject, and directive options on the options object
-
Handle extends and mixins on Options, incorporating them into the global configuration, respectively
-
Then merge the options configuration with the global configuration. If the options conflict, the options configuration overrides the global configuration
-
-
Interviewer: What did Vue.com Ponent (compName, Comp) do?
A:
Responsible for registering global components. The component configuration is registered with the global components option (options.com components), and then the sub-components will merge the global Components option into the local Components configuration when the vNode is generated.
-
If the second argument is empty, it indicates the component constructor that gets compName
-
If Comp is a component configuration object, use the vue. extend method to get the component constructor, otherwise go straight to the next step
-
Information on the global configuration Settings components, this.options.components.com pName = CompConstructor
-
-
Interviewer: What does vue. directive(‘my-directive’, {xx}) do?
A:
Register the mY-Directive globally, and then each of the child directives merges the global directives into the local directives when the VNode is generated. The principle is the same as Vue.com Ponent method:
-
If the second argument is empty, the configuration object for the specified directive is obtained
-
{bind: second argument, update: second argument}}
-
Then set the directive configuration object to the global configuration, this.options. Directives [‘my-directive’] = {xx}
-
-
What does vue.filter (‘my-filter’, function(val) {xx}) do?
A:
It is responsible for registering the mY-filter globally, and each subcomponent then merges the global filters option into the local filters option when generating the VNode. The principle is:
-
If the second argument is not provided, the callback function for the My-filter filter is obtained
-
Filters [‘my-filter’] = function(val) {xx} if the second argument is provided, set this.options. Filters [‘my-filter’] = function(val) {xx}.
-
-
Interviewer: What did Vue.extend(Options) do?
A:
Extend creates a subclass based on Vue, and the options parameter is the default global configuration for that subclass, just like the default global configuration for Vue. So extending a subclass with vue. extend is useful because it has built-in common configurations that can be used by subclasses of the subclass.
-
Define the subclass constructor, which, like Vue, calls _init(options).
-
Merge the Vue configuration and options. If the options conflict, the options overwrite the Vue configuration
-
Define a global API for a subclass with the value Vue global API, such as sub.extend = super.extend, so that the subclass can also extend to other subclasses
-
Return subclass Sub
-
-
Interviewer: What did Vue.set(target, key, val) do
A:
Due to the Vue cannot detect common new property (such as this. MyObject. NewProperty = ‘hi’), so by the Vue. Set to add a response to objects of type property, You can ensure that the new property is also responsive and triggers view updates.
-
Set (array, idx, val). Internally, the splice method is used to implement responsive updates
-
Set (obj, key,val) => obj[key] = val
-
You cannot dynamically add root-level responsive data to a Vue instance or $data
-
Vue.set(obj, key, val). If obj is not a reactive object, obj[key] = val will be executed, but no reactive processing will be done
-
Vue.set(obj, key, val), add a new key to the responsive object obj, set the responsive using defineReactive method and trigger the dependency update
-
-
Interviewer: What did Vue.delete(target, key) do?
A:
Delete the property of the object. If the object is reactive, make sure the deletion triggers an update to the view. This method is mainly used to get around the limitation that Vue cannot detect when a property is removed, but you should rarely use it. Of course, the same cannot be said for root level responsive attributes.
-
Vue.delete(array, idx), delete the element with the specified subscript, internally by splice
-
Delete an attribute on a responsive object: vue.delete (obj, key), internally delete obj.key, and then perform a dependency update
-
-
Interviewer: What did Vue.Nexttick (CB) do?
A:
NextTick (cb) ¶ The nextTick(cb) method is used to delay the execution of the cb callback function. This is used to retrieve DOM data immediately after this. Key = newVal is changed:
this.key = 'new val' Vue.nextTick(function() { // The DOM is updated }) Copy the code
Its internal implementation process is:
-
This.key = ‘new val, triggering the dependency notification update, placing the watcher responsible for the update in the watcher queue
-
Put the function that refreshes the Watcher queue into the Callbacks array
-
Put a function to refresh the array of Callbacks in the browser’s asynchronous task queue
-
NextTick (cb) to jump the queue and put the CB function into the callbacks array
-
A function to refresh the array of Callbacks is executed at some point in the future
-
It then executes a number of functions in the Callbacks array, triggering the execution of watcher.run and updating the DOM
-
Since the CB function is placed later in the Callbacks array, this ensures that the DOM update is completed before the CB function is executed
-
Form a complete set of video
Vue source code Interpretation (5) — Global API
Please focus on
Welcome everyone to follow my gold mining account and B station, if the content has to help you, welcome everyone to like, collect + attention
link
-
Vue source code interpretation (1) – preface
-
Vue source code interpretation (2) — Vue initialization process
-
Vue source code interpretation (3) – response principle
-
Vue source code interpretation (4) — asynchronous update
-
Vue source code Interpretation (5) — Global API
-
Vue source code interpretation (6) — instance method
-
(7) — Hook Event
-
Vue source code interpretation (8) — compiler parsing
-
Vue source code interpretation (9) — compiler optimization
-
Vue source code interpretation (10) — compiler generation rendering function
-
(11) — Render helper
-
Vue source code interpretation (12) — patch
Learning exchange group
link