Interpretation of Vue source code -- example method

Posted by jikishlove on Mon, 28 Feb 2022 01:45:32 +0100

When learning becomes a habit, knowledge becomes common sense. Thank you for your likes, collections and comments.

The new video and articles will be sent to WeChat official account for the first time. Li Yongning

The article has been included in github warehouse liyongning/blog , welcome to Watch and Star.

preface

Last article Interpretation of Vue source code (5) -- global API The implementation principle of each global API of Vue is introduced in detail. This article will introduce the implementation principle of each example method in detail.

target

Deeply understand the implementation principle of the following example method.

  • vm.$set
  • vm.$delete
  • vm.$watch
  • vm.$on
  • vm.$emit
  • vm.$off
  • vm.$once
  • vm._update
  • vm.$forceUpdate
  • vm.$destroy
  • vm.$nextTick
  • vm._render

Source code interpretation

entrance

/src/core/instance/index.js

This file is the entry file of Vue instance, including the definition of Vue constructor and the initialization of each instance method.

// Constructor for Vue
function Vue (options) {
  // Call Vue prototype._ Init method, which is defined in initMixin
  this._init(options)
}

// Define Vue prototype._ Init method
initMixin(Vue)
/**
 * definition:
 *   Vue.prototype.$data
 *   Vue.prototype.$props
 *   Vue.prototype.$set
 *   Vue.prototype.$delete
 *   Vue.prototype.$watch
 */
stateMixin(Vue)
/**
 * Define event related methods:
 *   Vue.prototype.$on
 *   Vue.prototype.$once
 *   Vue.prototype.$off
 *   Vue.prototype.$emit
 */
eventsMixin(Vue)
/**
 * definition:
 *   Vue.prototype._update
 *   Vue.prototype.$forceUpdate
 *   Vue.prototype.$destroy
 */
lifecycleMixin(Vue)
/**
 * Execute installRenderHelpers in Vue Install the runtime convenience program on the prototype object
 * 
 * definition:
 *   Vue.prototype.$nextTick
 *   Vue.prototype._render
 */
renderMixin(Vue)

vm.$data,vm.$props

src/core/instance/state.js

These are two instance properties, not instance methods. Here is a brief introduction. Of course, its implementation is also very simple

// data
const dataDef = {}
dataDef.get = function () { return this._data }
// props
const propsDef = {}
propsDef.get = function () { return this._props }
// Mount the data attribute and props attribute to Vue On the prototype object
// In this way, you can use this$ Data and this$ Props to access data and props objects
Object.defineProperty(Vue.prototype, '$data', dataDef)
Object.defineProperty(Vue.prototype, '$props', propsDef)

vm.$set

/src/core/instance/state.js

Vue.prototype.$set = set

set

/src/core/observer/index.js

/**
 * Via Vue Or set this$ The set method sets the value val for the specified key of the target
 * If the target is an object and the key does not exist, set the response for the new key, and then execute the 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)}`)
  }
  // Update the elements of the array with the specified subscript, Vue Set (array, IDX, Val), and realize the responsive update through the splice method
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
  }
  // Update the existing properties of the object, Vue Set (obj, key, Val) and execute the update
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  // You cannot add dynamic responsive attributes to Vue instances or $data, which is one of the uses of vmCount,
  // this.$data ob Vmcount = 1, indicating the VM of the root component and other sub components Vmcount is 0
  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 responsive object. The new attribute will be set, but it will not be processed in a responsive manner
  if (!ob) {
    target[key] = val
    return val
  }
  // Define a new attribute for the object, set the response through the defineReactive method, and trigger the dependency update
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}

vm.$delete

/src/core/instance/state.js

Vue.prototype.$delete = del

del

/src/core/observer/index.js

/**
 * Via Vue Delete or VM$ Delete deletes the specified key of the target object
 * The array is implemented through the splice method, and the object deletes the specified key through the delete operator and executes 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, delete the element of the specified subscript through the splice method
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.splice(key, 1)
    return
  }
  const ob = (target: any).__ob__

  // Avoid deleting the properties of Vue instances or the data of $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 through the delete operator
  delete target[key]
  if (!ob) {
    return
  }
  // Execute dependency notification
  ob.dep.notify()
}

vm.$watch

/src/core/instance/state.js

/**
 * Create a watcher and return to unwatch. Complete the following five things:
 *   1,Compatibility processing to ensure that cb in the last new Watcher is a function
 *   2,Mark user watcher
 *   3,Create a watcher instance
 *   4,If immediate is set, cb is executed immediately
 *   5,Return to unwatch
 * @param {*} expOrFn key
 * @param {*} cb Callback function
 * @param {*} options Configuration item, the user directly calls this$ A configuration item may be passed when watching
 * @returns Returns the unwatch function, which is used to cancel watch listening
 */
Vue.prototype.$watch = function (
  expOrFn: string | Function,
  cb: any,
  options?: Object
): Function {
  const vm: Component = this
  // Compatibility processing because the user calls VM$ The cb set during watch may be an object
  if (isPlainObject(cb)) {
    return createWatcher(vm, expOrFn, cb, options)
  }
  // options.user refers to the user watcher, as well as the render watcher, that is, the watcher instantiated in the updateComponent method
  options = options || {}
  options.user = true
  // Create a watcher
  const watcher = new Watcher(vm, expOrFn, cb, options)
  // If the user sets immediate to true, the callback function is executed immediately
  if (options.immediate) {
    try {
      cb.call(vm, watcher.value)
    } catch (error) {
      handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
    }
  }
  // Returns an unwatch function, which is used to cancel listening
  return function unwatchFn() {
    watcher.teardown()
  }
}

vm.$on

/src/core/instance/events.js

const hookRE = /^hook:/
/**
 * Listen for custom events on the instance, VM_ event = { eventName: [fn1, ...], ... }
 * @param {*} event A single event name or an array of multiple event names
 * @param {*} fn Callback function executed when event is triggered
 * @returns 
 */
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
  const vm: Component = this
  if (Array.isArray(event)) {
    // Event is an array composed of multiple event names, then traverse these events and call $on recursively in turn
    for (let i = 0, l = event.length; i < l; i++) {
      vm.$on(event[i], fn)
    }
  } else {
    // Store the registered events and callbacks in the form of key value pairs in VM_ VM. In event object_ event = { eventName: [fn1, ...] }
    (vm._events[event] || (vm._events[event] = [])).push(fn)
    // hookEvent, which provides an opportunity to inject the declaration cycle method into the component instance from the outside
    // For example, inject additional logic into the mounted method of the component from outside the component
    // This capability is implemented in combination with the callhook method
    if (hookRE.test(event)) {
      vm._hasHookEvent = true
    }
  }
  return vm
}

About hookEvent, the next article will introduce it in detail.

vm.$emit

/src/core/instance/events.js

/**
 * Trigger the specified event on the instance, VM_ event[event] => cbs => loop cbs => cb(args)
 * @param {*} event Event name
 * @returns 
 */
Vue.prototype.$emit = function (event: string): Component {
  const vm: Component = this
  if (process.env.NODE_ENV !== 'production') {
    // Convert the event name to smaller
    const lowerCaseEvent = event.toLowerCase()
    // This means that HTML attributes are not case sensitive, so you can't use v-on to listen for event names in the form of humps. Instead, you should use event names in the form of hyphens
    if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
      tip(
        `Event "${lowerCaseEvent}" is emitted in component ` +
        `${formatComponentName(vm)} but the handler is registered for "${event}". ` +
        `Note that HTML attributes are case-insensitive and you cannot use ` +
        `v-on to listen to camelCase events when using in-DOM templates. ` +
        `You should probably use "${hyphenate(event)}" instead of "${event}".`
      )
    }
  }
  // From VM_ Get the callback function array of the current event from the event object, call the callback function in the array at one time, and pass the provided parameters
  let cbs = vm._events[event]
  if (cbs) {
    cbs = cbs.length > 1 ? toArray(cbs) : cbs
    const args = toArray(arguments, 1)
    const info = `event handler for "${event}"`
    for (let i = 0, l = cbs.length; i < l; i++) {
      invokeWithErrorHandling(cbs[i], vm, args, vm, info)
    }
  }
  return vm
}

vm.$off

/src/core/instance/events.js

/**
 * Remove the custom event listener, that is, from VM_ Find the corresponding event in the event object, remove all events or remove the callback function of the specified event
 * @param {*} event 
 * @param {*} fn 
 * @returns 
 */
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
  const vm: Component = this
  // vm.$off() removes all listeners on the instance = > VM_ events = {}
  if (!arguments.length) {
    vm._events = Object.create(null)
    return vm
  }
  // Remove some events event = [event1,...], Traverse the event array and recursively call VM$ off
  if (Array.isArray(event)) {
    for (let i = 0, l = event.length; i < l; i++) {
      vm.$off(event[i], fn)
    }
    return vm
  }
  // Except VM$ All except off () will eventually come here and remove the specified event
  const cbs = vm._events[event]
  if (!cbs) {
    // Indicates that the event has not been registered
    return vm
  }
  if (!fn) {
    // If fn callback function is not provided, remove all callback functions of this event, VM_ event[event] = null
    vm._events[event] = null
    return vm
  }
  // To remove the specified callback function of a specified event is to find the callback function from the callback array of the event and then delete it
  let cb
  let i = cbs.length
  while (i--) {
    cb = cbs[i]
    if (cb === fn || cb.fn === fn) {
      cbs.splice(i, 1)
      break
    }
  }
  return vm
}

vm.$once

/src/core/instance/events.js

/**
 * Listen to a custom event, but only trigger it once. Once triggered, the listener is removed
 * vm.$on + vm.$off
 * @param {*} event 
 * @param {*} fn 
 * @returns 
 */
Vue.prototype.$once = function (event: string, fn: Function): Component {
  const vm: Component = this

  // Call $on, but the callback function of $on is specially handled. When triggered, execute the callback function, remove the event listener, and then execute the callback function you set
  function on() {
    vm.$off(event, on)
    fn.apply(vm, arguments)
  }
  on.fn = fn
  vm.$on(event, on)
  return vm
}

vm._update

/src/core/instance/lifecycle.js

/**
 * It is responsible for updating the page. The entry location of the first rendering and subsequent updates of the page is also the entry location of the patch 
 */
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
  const vm: Component = this
  const prevEl = vm.$el
  const prevVnode = vm._vnode
  const restoreActiveInstance = setActiveInstance(vm)
  vm._vnode = vnode
  // Vue.prototype.__patch__ is injected in entry points
  // based on the rendering backend used.
  if (!prevVnode) {
    // This is the first rendering, that is, when initializing the page
    vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
  } else {
    // When the responsive data is updated, that is, when the page is updated, go here
    vm.$el = vm.__patch__(prevVnode, vnode)
  }
  restoreActiveInstance()
  // update __vue__ reference
  if (prevEl) {
    prevEl.__vue__ = null
  }
  if (vm.$el) {
    vm.$el.__vue__ = vm
  }
  // if parent is an HOC, update its $el as well
  if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
    vm.$parent.$el = vm.$el
  }
  // updated hook is called by the scheduler to ensure that children are
  // updated in a parent's updated hook.
}

vm.$forceUpdate

/src/core/instance/lifecycle.js

/**
 * Call watcher directly Update method to force the component to re render.
 * It only affects the instance itself and the subcomponents inserted into the slot content, not all subcomponents
 */
Vue.prototype.$forceUpdate = function () {
  const vm: Component = this
  if (vm._watcher) {
    vm._watcher.update()
  }
}

vm.$destroy

/src/core/instance/lifecycle.js

/**
 * Completely destroy an instance. Clean up its connection with other instances and unbind all its instructions and event listeners.
 */
Vue.prototype.$destroy = function () {
  const vm: Component = this
  if (vm._isBeingDestroyed) {
    // Indicates that the instance has been destroyed
    return
  }
  // Call beforeDestroy hook
  callHook(vm, 'beforeDestroy')
  // The identity instance has been destroyed
  vm._isBeingDestroyed = true
  // Remove yourself from your father ($parent) ($children)
  const parent = vm.$parent
  if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
    remove(parent.$children, vm)
  }
  // Remove dependent listening
  if (vm._watcher) {
    vm._watcher.teardown()
  }
  let i = vm._watchers.length
  while (i--) {
    vm._watchers[i].teardown()
  }
  // remove reference from data ob
  // frozen object may not have observer.
  if (vm._data.__ob__) {
    vm._data.__ob__.vmCount--
  }
  // call the last hook...
  vm._isDestroyed = true
  // Call__ patch__, Destroy node
  vm.__patch__(vm._vnode, null)
  // Call destroyed hook
  callHook(vm, 'destroyed')
  // Turn off all event listening of the instance
  vm.$off()
  // remove __vue__ reference
  if (vm.$el) {
    vm.$el.__vue__ = null
  }
  // release circular reference (#6759)
  if (vm.$vnode) {
    vm.$vnode.parent = null
  }
}

vm.$nextTick

/src/core/instance/render.js

Vue.prototype.$nextTick = function (fn: Function) {
  return nextTick(fn, this)
}

nextTick

/src/core/util/next-tick.js

const callbacks = []
/**
 * Accomplish two things:
 *   1,Wrap the flushSchedulerQueue function with try catch and put it into the callbacks array
 *   2,If pending is false, it means that there is no flushCallbacks function in the browser's task queue
 *     If pending is true, it indicates that the flushCallbacks function has been put into the browser's task queue,
 *     When the flushCallbacks function is to be executed, pending will be set to false again, indicating that the next flushCallbacks function can enter
 *     The browser's task queue
 * pending Function: ensure that there is only one flushCallbacks function in the browser's task queue at the same time
 * @param {*} cb Receive 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 try catch to facilitate error capture
      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 (micro task queue is preferred)
    timerFunc()
  }
  // $flow-disable-line
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

vm._render

/src/core/instance/render.js

/**
 * Generate VNode by executing render function
 * However, a lot of exception handling code is added
 */
Vue.prototype._render = function (): VNode {
  const vm: Component = this
  const { render, _parentVnode } = vm.$options

  if (_parentVnode) {
    vm.$scopedSlots = normalizeScopedSlots(
      _parentVnode.data.scopedSlots,
      vm.$slots,
      vm.$scopedSlots
    )
  }

  // Set the parent vnode. This allows the rendering function to access the data on the placeholder node.
  vm.$vnode = _parentVnode
  // render self
  let vnode
  try {
    currentRenderingInstance = vm
    // Execute the render function to generate vnode
    vnode = render.call(vm._renderProxy, vm.$createElement)
  } catch (e) {
    handleError(e, vm, `render`)
    // Here, it means that an error occurred while executing the render function
    // The development environment renders the error message, and the production environment returns the previous vnode to prevent the rendering error from leading to blank components
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {
      try {
        vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
      } catch (e) {
        handleError(e, vm, `renderError`)
        vnode = vm._vnode
      }
    } else {
      vnode = vm._vnode
    }
  } finally {
    currentRenderingInstance = null
  }
  // If the returned vnode is an array and contains only one element, it will be flattened directly
  if (Array.isArray(vnode) && vnode.length === 1) {
    vnode = vnode[0]
  }
  // When the render function makes an error, it returns an empty vnode
  if (!(vnode instanceof VNode)) {
    if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
      warn(
        'Multiple root nodes returned from render function. Render function ' +
        'should return a single root node.',
        vm
      )
    }
    vnode = createEmptyVNode()
  }
  // set parent
  vnode.parent = _parentVnode
  return vnode
}

installRenderHelpers

src/core/instance/render-helpers/index.js

This method is responsible for installing a large number of short tool functions related to rendering on the instance. These tool functions are used in the rendering functions generated by the compiler, such as VM after v-for compilation_ l. There is also the most familiar h function (vm._c), but it is not declared here. It is declared in the initRender function.

The installRenderHelpers method is called in renderMixin.

/**
 * Mount the shorthand rendering tool function on the instance
 * @param {*} target Vue example
 */
export function installRenderHelpers (target: any) {
  target._o = markOnce
  target._n = toNumber
  target._s = toString
  target._l = renderList
  target._t = renderSlot
  target._q = looseEqual
  target._i = looseIndexOf
  target._m = renderStatic
  target._f = resolveFilter
  target._k = checkKeyCodes
  target._b = bindObjectProps
  target._v = createTextVNode
  target._e = createEmptyVNode
  target._u = resolveScopedSlots
  target._g = bindObjectListeners
  target._d = bindDynamicKeys
  target._p = prependModifier
}

If you are interested in a certain method, you can delve into it yourself.

summary

  • The interviewer asked: VM$ What did set (obj, key, Val) do?

    Answer:

    vm.& dollar; Set is used to add a new property to the responsive object, ensure that the new property is also responsive, and trigger the view update. Because Vue cannot detect the new attributes of the object or add an element to the array through the index, such as this obj. newProperty = 'val',this.arr[3] = 'val'. That's why VM$ Set, which is Vue Alias of set.

    • Add a new responsive data for the object: call the defineReactive method to add responsive data for the object, and then execute dep.notify to notify the dependency and update the view
    • Add a new responsive data to the array: through the splice method

  • The interviewer asked: VM$ What does delete (obj, key) do?

    Answer:

    vm.$delete is used to delete attributes on an object. If the object is responsive and can ensure that the view update can be triggered. This method is mainly used to avoid the situation that Vue cannot detect the deletion of attributes. It's Vue Alias of delete.

    • Delete the elements of the specified subscript of the array, which is completed internally through the splice method
    • To delete a specified attribute on an object, first delete the attribute through the delete operator, and then execute dep.notify to notify the dependency and update the view

  • The interviewer asked: VM$ What does watch (exporfn, callback, [options]) do?

    Answer:

    vm.$watch is responsible for observing the change of the calculation result of an expression or a function on the Vue instance. When it changes, the callback function will be executed and two parameters will be passed to the callback function. The first is the updated new value and the second is the old value.

    One thing to note here is: if you are observing an object, such as an array, when you add an element to the array with array methods, such as push, the new value passed by the callback function is the same as the old value when triggered, because they point to the same reference, Therefore, when observing an object and judging whether the new and old values are equal in the callback function, we need to pay attention to it.

    vm.$ The first parameter of watch only receives the key path of simple responsive data. For more complex expressions, it is recommended to use the function as the first parameter.

    As for VM$ The internal principle of watch is:

    • Set options User = true, the flag is a user watcher
    • Instantiate a watcher instance. When a data update is detected, trigger the execution of the callback function through the watcher, and pass the new and old values as the parameters of the callback function
    • Returns an unwatch function to cancel observation

  • The interviewer asked: VM$ What did on (event, callback) do?

    Answer:

    Listens to user-defined events on the current instance. Events can be generated by VM& dollar; When triggered by emit, the callback function will receive all the additional parameters of the incoming event trigger function (vm.$emit).

    vm.$ The principle of on is very simple, which is to handle the two parameters of event and callback passed, and store the registered event and callback function in VM in the form of key value pairs_ In the event object, VM_ events = { eventName: [cb1, cb2, ...], ... }.

  • The interviewer asked: VM$ What did emit (eventName, [... Args]) do?

    Answer:

    Trigger the specified event on the current instance, and the additional parameters will be passed to the callback function of the event.

    Its internal principle is to execute VM_ All callback functions in events [eventName].

    It can be seen from the implementation principle of $Emon at this time, so it depends on who can trigger the event Interpretation of Vue source code (2) -- Vue initialization process The explanation of initEvent in will understand what is being said, because VM is used internally for the processing of component custom events$ on,vm.$emit.

  • The interviewer asked: VM$ What does off ([event, callback]) do?

    Answer:

    Remove the custom event listener, that is, remove VM_ Relevant data on the events object.

    • If no parameters are provided, all event listeners of the instance will be removed
    • If only the event parameter is provided, all listeners for the event on the instance are removed
    • If both parameters are provided, remove the listener corresponding to the event on the instance

  • The interviewer asked: VM$ What does once (event, callback) do?

    Answer:

    Listen for a custom event, but the event will only be triggered once. Once triggered, the listener is removed.

    Its internal implementation principle is:

    • Wrap the callback function passed by the user. When the wrapper function is executed, in addition to the user callback function, it will also execute VM$ Off (event, wrapper function) removes the event
    • Use VM$ On (event, wrapper function) registers events

  • The interviewer asked: VM_ What does update (vnode, hydrating) do?

    Answer:

    The official document does not specify the API. This is an instance method used in the internal source code. It is responsible for updating the page. It is the entry of page rendering. It determines whether to render for the first time or update the page according to the existence of prevVnode\_\_ patch\_\_ Function passes different parameters. This method will not be used in business development.

  • The interviewer asked: VM$ What does forceupdate () do?

    Answer:

    Force the Vue instance to re render, which only affects the component instance itself and the sub components inserted into the slot content, not all sub components. Its internal principle is simple, that is, directly call VM_ watcher.update(), which is the watcher Update() method, execute this method to trigger component update.

  • The interviewer asked: VM$ What did destroy () do?

    Answer:

    Responsible for completely destroying an instance. Clean up its connection with other instances and unbind all its instructions and event listeners. During execution, two hook functions beforeDestroy and destroy will be called. This method is not used in most business development scenarios, and is generally operated through v-if instructions. Its internal principle is:

    • Call beforeDestroy hook function
    • Remove yourself from dad ($parent) and destroy your relationship with dad
    • Through the watcher Teardown() to remove dependency listening
    • Via VM\_\_ patch\_\_ (vnode, null) method to destroy the node
    • Call the destroyed hook function
    • Via VM$ The off method removes all event listeners

  • The interviewer asked: VM$ What did nexttick (CB) do?

    Answer:

    vm.$nextTick is Vue The alias of nexttick, whose function is to delay the execution of callback function cb, is generally used for this Key = newval after changing the data, you want to get the changed DOM data immediately:

    this.key = 'new val'
    
    Vue.nextTick(function() {
      // DOM updated
    })

    Its internal execution process is:

    • this.key = 'new val', trigger the update of dependency notification and put the watcher responsible for the update into the watcher queue
    • Put the function of refreshing the watcher queue into the callbacks array
    • Put a function to refresh the callbacks array in the asynchronous task queue of the browser
    • vm.$nextTick(cb) to jump the queue and put the cb function directly into the callbacks array
    • The function of refreshing the callbacks array will be executed at a certain time in the future
    • Then execute many functions in the callbacks array to trigger the watcher Execute run and update DOM
    • Since the cb function is put into the callbacks array later, this ensures that the DOM update is completed first, and then the cb function is executed

  • The interviewer asked: VM_ What did render do?

    Answer:

    The official document does not provide this method. It is an instance method used in the source code, which is responsible for generating vnode. The key code is just one line, and the render function is executed to generate vnode. However, a lot of exception handling code is added.

link

Thank you for your likes, collections and comments. I'll see you next time.

When learning becomes a habit, knowledge becomes common sense. Thank you for your likes, collections and comments.

The new video and articles will be sent to WeChat official account for the first time. Li Yongning

The article has been included in github warehouse liyongning/blog , welcome to Watch and Star.

Topics: Javascript Front-end ECMAScript TypeScript Vue.js