current position:Home>Source code analysis, Vue global API set, del, nexttick, use, mixin, etc
Source code analysis, Vue global API set, del, nexttick, use, mixin, etc
2022-04-29 04:28:10【@huihui_ new】
stay vue In the source src/core/global-api The directory index Next , Global is initialized API, as follows :
initGlobalAPI
import config from '../config'
import {
initUse } from './use'
import {
initMixin } from './mixin'
import {
initExtend } from './extend'
import {
initAssetRegisters } from './assets'
import {
set, del } from '../observer/index'
import {
ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import {
warn,
extend,
nextTick,
mergeOptions,
defineReactive
} from '../util/index'
export function initGlobalAPI (Vue: GlobalAPI) {
// 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)
// 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
}
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
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)
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}
You can see , stay initGlobalAPI First of all Vue Added some config Configuration properties , If these configurations are modified, a warning will be reported , This is important to the overall situation API No impact analysis . The next in Vue.util Added some methods to the properties of , Can pass Vue.util.[xx] To access these methods , But it's not recommended ,util The methods in may change from time to time , Of course, this is important to the overall situation API The analysis has no effect .
Next, give the constructor Vue Initialized some static API,set、delete、nextTick These methods
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
Vue.options._base = Vue
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]
This code is for Vue Of options Some objects are initialized on the object , give the result as follows :
adopt extend function , to Vue.components To add the keepAlive Components , That's why we can use it directly keepAlive The cause of the component
export function extend (to: Object, _from: ?Object): Object {
for (const key in _from) {
to[key] = _from[key]
}
return to
}
Next, execute some initialization methods , In the constructor Vue To add the use、mixin、extend、components、directive、filter These methods . Next, the global method of mounting is analyzed in detail .
1.set
/** * Set a property on an object. Adds the new property and * triggers change notification if the property doesn't * already exist. */
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)}`)
}
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
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
}
if (!ob) {
target[key] = val
return val
}
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
export function isUndef (v: any): boolean %checks {
return v === undefined || v === null
}
export function isPrimitive (value: any): boolean %checks {
return (
typeof value === 'string' ||
typeof value === 'number' ||
// $flow-disable-line
typeof value === 'symbol' ||
typeof value === 'boolean'
)
}
set Method can only work on arrays or objects , If the property value set is undefined, null, or primitive value A warning will be given , Next, judge the set properties , If it already exists in the array or object , Give a new value and return ;
Next, let's see if the object contains attributes __ob__( Flag indicating whether the object has listened ), The monitored object cannot be a vue example , Otherwise, the report will be wrong ;
If the object is not listening , The value is returned directly ,
defineReactive(ob.value, key, val)
ob.dep.notify()
If everything is OK , Add a responsive attribute to the object ( adopt defineReactive), And notify the dependent ( adopt notify) Make corresponding changes , These two methods are important methods of the response principle , I won't elaborate here , But from here we can see ,Vue.set This method is to set the dynamic new properties of an existing object to be responsive .
( Dynamically adding methods to objects or arrays , No, set Under the circumstances , Is not responsive , The author once stepped on this pit , So it's still necessary to learn the source code !!!)
2.delete
/** * Delete a property and trigger change if necessary. */
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 (Array.isArray(target) && isValidArrayIndex(key)) {
target.splice(key, 1)
return
}
const ob = (target: any).__ob__
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 (!hasOwn(target, key)) {
return
}
delete target[key]
if (!ob) {
return
}
ob.dep.notify()
}
delete Methods and set The method is very similar , The previous logic is similar to set It's the same , If this property is not included in the object , Then return directly , Otherwise, through native Syntax delete This attribute , If there is __ob__ object , Then notify the dependency to make corresponding changes ;
Vue.prototype.$set = set
Vue.prototype.$delete = del
There is also the above code in the source code , Use the prototype to mount set and del Method , That's why we can use this.$xx To call set and del.
3.nextTick
js The execution of is single threaded , The execution process of the main thread is a tick, And all asynchronous results come through “ Task queue ” To dispatch , This involves macro tasks and micro tasks , In the browser environment , common macro task Yes setTimeout、MessageChannel、postMessage、setImmediate; common micro task Yes MutationObsever and Promise.then, I won't elaborate here .
Let's look at nextTick Source code :
const callbacks = []
let pending = false
export function nextTick (cb?: Function, ctx?: Object) {
let _resolve
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
_resolve(ctx)
}
})
if (!pending) {
pending = true
if (useMacroTask) {
macroTimerFunc()
} else {
microTimerFunc()
}
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
First abandon _resolve The logic of ( This is to achieve promise Preparing for , As we'll see ),
This function will first send a message to callbacks Push an anonymous function into the array , When the anonymous function is executed, it will trigger the incoming callback, To determine pending, This pending At first it was false, After entering, it is assigned as true, That is to ensure that this logic will only be executed once , To determine useMacroTask, According to browser support , Determine different ways of execution .
// Here we have async deferring wrappers using both microtasks and (macro) tasks.
// In < 2.4 we used microtasks everywhere, but there are some scenarios where
// microtasks have too high a priority and fire in between supposedly
// sequential events (e.g. #4521, #6690) or even between bubbling of the same
// event (#6566). However, using (macro) tasks everywhere also has subtle problems
// when state is changed right before repaint (e.g. #6813, out-in transitions).
// Here we use microtask by default, but expose a way to force (macro) task when
// needed (e.g. in event handlers attached by v-on).
let microTimerFunc
let macroTimerFunc
let useMacroTask = false
// Determine (macro) task defer implementation.
// Technically setImmediate should be the ideal choice, but it's only available
// in IE. The only polyfill that consistently queues the callback after all DOM
// events triggered in the same loop is by using MessageChannel.
/* istanbul ignore if */
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
macroTimerFunc = () => {
setImmediate(flushCallbacks)
}
} else if (typeof MessageChannel !== 'undefined' && (
isNative(MessageChannel) ||
// PhantomJS
MessageChannel.toString() === '[object MessageChannelConstructor]'
)) {
const channel = new MessageChannel()
const port = channel.port2
channel.port1.onmessage = flushCallbacks
macroTimerFunc = () => {
port.postMessage(1)
}
} else {
/* istanbul ignore next */
macroTimerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
// Determine microtask defer implementation.
/* istanbul ignore next, $flow-disable-line */
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
microTimerFunc = () => {
p.then(flushCallbacks)
// in problematic UIWebViews, Promise.then doesn't completely break, but
// it can get stuck in a weird state where callbacks are pushed into the
// microtask queue but the queue isn't being flushed, until the browser
// needs to do some other work, e.g. handle a timer. Therefore we can
// "force" the microtask queue to be flushed by adding an empty timer.
if (isIOS) setTimeout(noop)
}
} else {
// fallback to macro
microTimerFunc = macroTimerFunc
}
/** * Wrap a function so that if any code inside triggers state change, * the changes are queued using a (macro) task instead of a microtask. */
export function withMacroTask (fn: Function): Function {
return fn._withTask || (fn._withTask = function () {
useMacroTask = true
const res = fn.apply(null, arguments)
useMacroTask = false
return res
})
}
You can see that the priority will be to check whether the browser supports native setImmediate, If it is not supported, then check whether it supports native MessageChannel, If it doesn't support it, it will be downgraded to setTimeout 0; And for micro task The implementation of the , Then check whether the browser supports native Promise, If not, point directly to macro task The implementation of the , In fact, these functions will eventually execute flushCallbacks.
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
You can see ,flushCallbacks Lieutenant general pending Assigned to false, Then the next... Is executed in turn tick All of them callback.
Besides , go back to _resolve Variable , In addition to the way of incoming callbacks ,nextTick Also support promise.then, When there is no cb When it comes in , Will execute :
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
The next tick When it is executed, it will go to else if Logic , You can go through .then The way to execute .
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
_resolve(ctx)
}
From the above we can see that ,nextick Function is to collect all callback functions and put them in the next tick To execute .
$nextTick
Vue.prototype.$nextTick = function (fn: Function) {
return nextTick(fn, this)
}
In the constructor Vue The prototype is also mounted nextTick Method , That's why we can also use this.$nextTick To call nextTick Why .
4.use、mixin
Vue.use And mixin You can go to my other source code analysis From initialization vuex From the perspective of use、mixin, Inside through to Vue.use(Store) Process analysis , Yes use、mixin Your understanding is more vivid and specific .
about use function , In fact, the core is to execute the incoming plug-in install Method :
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// additional parameters
const args = toArray(arguments, 1)
args.unshift(this)
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
installedPlugins.push(plugin)
return this
}
}
about mixin function , The core is the implementation of mergeOptions function , That is to say, to Vue.options add to mixin object
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}
among ,mergeOptions There are different merging strategies for different attributes in the function , For example, hook function
/** * Merge two option objects into a new one. * Core utility used in both instantiation and inheritance. */
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
}
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
const extendsFrom = child.extends
if (extendsFrom) {
parent = mergeOptions(parent, extendsFrom, 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
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
LIFECYCLE_HOOKS.forEach(hook => {
strats[hook] = mergeHook
})
function mergeHook (
parentVal: ?Array<Function>, childVal: ?Function | ?Array<Function>
): ?Array<Function> {
return childVal
? parentVal
? parentVal.concat(childVal)
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal
}
For hook functions , Will pass parentVal.concat(childVal) Merge into an array and mix into Vue.options On the object , The execution order is from front to back .
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
For the incoming mixin Objects have mixins Attribute , First the mixins The hook function in the property is merged into parent Back , Re merger mixin Hook function in object , The final execution sequence is :
Vue.options The hook function above –>mixin On the object mixins Hook function on property object –>mixin Hook function on object
5.extend
... To be continued
copyright notice
author[@huihui_ new],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2022/117/202204270551339253.html
The sidebar is recommended
- vue2. How to register and login with 0 + koa2 + mongodb
- How to set the background color for individual pages in Vue cli
- Vue2 how to load more data on the scroll bar
- How to use margin and padding in CSS
- How does vue2x implement the vueslidershow function of the responsive adaptive rotation component plug-in
- How does vue2 recursive component implement tree menu
- How to use the three selectors of CSS
- Same blog, different styles -- hexo alternative construction
- Optimize nginx HTTPS latency - see how I can speed up nginx by 30%?
- The development model in the era of consumer Internet only transfers industrial elements from offline to online
guess what you like
Advanced features of Vue
CSS shadow advanced, to achieve a more three-dimensional shadow effect!
Introduction to the number type method in JavaScript
Differences between nginx forward and reverse proxy
Front end schema form configuration generation scheme
Units that can be used by the rotate method of transform in CSS
Why CSS3 sticky doesn't work?
[bug resolution] webpack error: module not found Did you mean xxx
[bug resolution] can't perform a react state update on an unmounted component This is > a no-op, but it...
Remember to speed up the construction of a webpack
Random recommended
- Browser review hover element
- Vue insert iframe
- Vue realizes downloading pictures
- Some common problem-solving methods in Vue (sort out some truly effective methods that can be used in the project) (wait for continuous update and accumulation)
- About database data response to HTML page, Chinese appears? Solution to garbled code (multiple question marks)
- Ask a question about CSS selector
- When Vue jumps the route, the timer cannot be cleared in time
- Document is not defined
- Vuepress packaging deployment stepping on the road of the pit
- Vue quill editor stepping on pit record -- the echo style of rich text content is wrong
- The multi selection box of table in element is combined with paging to echo the bug step hole
- Vue project optimizes the loading speed of the first screen
- Use / deep / to report an error CSS (CSS selected expected)
- Nginx configuration
- Vue cli reverse proxy
- Vuex persistedstate plug-in - vuex persistent storage
- Automatic import of less
- Understanding and summary of CSS
- vue2. Implementation of X data response
- JavaScript content understanding
- Props in react
- JavaScript stack“
- Bootstrap blazor table component (III) intelligent generation
- Based on centos7 + nginx + Daphne + uwsgi + django3 2 + supervisor + mysql8 single architecture server production environment deployment (I)
- Differences between vue2 and vue3 named slots
- Vue3: Axios cross domain request problem
- The difference between vue2 and vue3: keep alive
- Configure nginx and SSL certificate for Django project under Windows Environment
- Ant Design Vue: a-table custom column
- Using jQuery in Vue
- Vue dynamic loading picture problem
- Icons using Alibaba vector icon library in Vue
- Java Android mobile phone automatic basic learning element positioning
- Rancher configuring HTTPS domain name to access graphic tutorial
- Building a blog with GitHub pages + hexo (7) how to delete a published article successfully solved: delete it at the same time deploy_ Git folder
- Eight blog views / hexubs
- Build a blog with GitHub pages + hexo (9) set the next theme of hexo blog, and only part of the home page is displayed (not the full text)
- Building a blog with GitHub pages + hexo (10) the next theme of hexo blog mathjax mathematical formula rendering problem
- Hexo/Github. IO configure Tencent cloud CDN
- Details of Vue to react useeffect