Previously on & Background

The mountComponent () method creates the render Watcher (). This method is called the mountComponent () method. This method creates the render Watcher ().

This paper focuses on how to mount VNode to the page through patch. The key methods involved are vm._render and vm._update, which are two methods on Vue prototype objects.

The so-called patch in Vue refers to the process of turning VNode into real DOM and rendering to the page. This process includes both the initial rendering and the process of updating the view due to responsive data update after the initial rendering.

Review the process of creating a render Watcher

2.1 mountComponent

MountComponent creates render Watcher, creates render Watcher;

export function mountComponent () :Component {


  let updateComponent
  
  if(...). {}else {
    // The second argument passed to the Watcher constructor
    updateComponent = () = > {
      vm._update(vm._render(), hydrating)
    }
  }
   
  / / render watcher
  new Watcher(vm, updateComponent, noop, {
    before () {
      if(vm._isMounted && ! vm._isDestroyed) { callHook(vm,'beforeUpdate')}}},true /* isRenderWatcher */)
 
  return vm
}
Copy the code

2.2 Watcher class

export default class Watcher {
  constructor (
    vm: Component,
    expOrFn: string | Function.// updateComponent passed in by the mountComponent method above
    cb: Function,
    options?: ?Object, isRenderWatcher? : boolean) {
    / / expOrFn is
    // updateComponent = () => vm._update(vm._render(), hy...)
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    }

    this.value = this.lazy
      ? undefined
      : this.get()
  }
  
  get () {
    try {
      // Execute the callback function, such as updateComponent, to enter the patch phase
      value = this.getter.call(vm, vm)
    } catch (e) {
    }
    return value
  } 
}
Copy the code

New Watcher will call this.get() and then call updateComponent, whose method will call vm._render() to get VNode and pass it to vm._update to enter patch phase. Next, let’s look at vm._render and vm._update

let updateComponent = () = > vm._update(vm._render(), hydrating)
Copy the code

2.3 the Vue. Prototype. And _render method

Methods location: SRC/core/instance/render. Js – > function renderMix – > Vue. Prototype. And _render

Method parameters: None

The render method generates a VNode by executing the render function and adding error handling logic, which is always ignored in the following code. The generated VNode will be passed to the vm._update method;

export function renderMixin (Vue: Class<Component>) {
  // Render function help function registration, _l/_t/_v/_s...
  installRenderHelpers(Vue.prototype)


  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
      )
    }

    
    // Sets the parent vNode, which allows the rendering function to access the data on the placeholder node
    vm.$vnode = _parentVnode
   
    // Start render function
    let vnode
    try {
      // There's no need to maintain a stack because all render fns are called
      // separately from one another. Nested component's render fns are called
      // when parent component is patched.
      // Execute render function to generate vnode; There's no need to maintain a stack here,
      // This is because each render function is called separately,
      // The nested component's render function is called after the parent component is patched
      
      currentRenderingInstance = vm
      // render with (this) {}
      // vm._renderProxy is this,
      // is the source of the data referenced in the rendering function, or the VM itself in production
      vnode = render.call(vm._renderProxy, vm.$createElement)
    } catch (e) {
      // ...
    } finally {
      currentRenderingInstance = null
    }
   
    // If the returned vNode is an array and has only one item, return that item directly
    if (Array.isArray(vnode) && vnode.length === 1) {
      vnode = vnode[0]}// set parent
    vnode.parent = _parentVnode
    return vnode
  }
Copy the code

Vue. Prototype. _update method

Methods location: SRC/core/instance/lifecycle. Js – > function linfecycleMixin – > Vue. Prototype. _update

Method parameters: vnode, vnode instance, also known as the virtual DOM tree;

Function of method: responsible for updating the page, including the first rendering of the page and the entrance of updating the page after data changes, as well as the entrance position of patch

export function lifecycleMixin (Vue: Class<Component>) {

  Vue.prototype._update = function (vnode: VNode, hydrating? : boolean) {
    const vm: Component = this

    // Mount point of the page, real element
    const prevEl = vm.$el

    / / the old VNode
    // When will old vNodes be available?
    // Of course, when the data changes and needs to be updated, the first rendered VNode is the old VNode obtained during the update
    // prevVnode holds the old VNode temporarily. DOM Diff compares the old and the new
    const prevVnode = vm._vnode
    const restoreActiveInstance = setActiveInstance(vm)

    / / new VNode
    vm._vnode = vnode


    // vue.prototype. __patch__ is injected in the Vue entry module
    if(! prevVnode) {// The old VNode does not exist

      // Go here when initializing the page for the first rendering
      // initial render
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)}else {
      // The process of updating the view as a result of reactive data updates is else
      // vue.prototype. __patch__ is injected in the entry module
      vm.$el = vm.__patch__(prevVnode, vnode) 
    }

    restoreActiveInstance()
    
    // Update the __vue__ reference
    if (prevEl) {
      // Unreference the old VNode to the VM
      prevEl.__vue__ = null
    }
    if (vm.$el) {
      vm.$el.__vue__ = vm
    }
    
    // if parent is an HOC, update its $el as well
    If the parent node is a higher-order component, update its $el
    if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
      vm.$parent.$el = vm.$el
    }
    
    // Updated lifecycle hooks will be called on scheduler,
    // To ensure that the child's updated is invoked during the parent's updated call}}Copy the code

Third, Vue. Prototype.patch

Methods location: SRC/platforms/web/runtime/index. The js – > Vue. Prototype. __patch__

import { patch } from './patch'
// Install patch function of web platform on Vue prototype.
Vue has cross-platform capabilities weex, so the emphasis here is on the Web platform
Vue.prototype.__patch__ = inBrowser ? patch : noop
Copy the code

3.1 patch

The patch method assigned to vue.prototype. __patch__ above is the result of the createPatchFunction factory function below;

// Patch factory function, passing platform-specific operations for it, and then returning a patch function
export const patch: Function = createPatchFunction({ nodeOps, modules })
Copy the code

3.2 createPatchFunction

SRC /core/vdom/patch.js -> function createPatchFunction

Method parameters:

  1. backend, this is a configuration object, inwebPlatforms have two attributes:{ nodeOps, modules }
    • 1.1 nodeOps, from SRC/platforms/web/runtime/node – ops. Js, which contains the createElement method, the appendChild, insertBefore encapsulation of browser DOM API, These are all real DOM manipulations, but we’ll talk about them later;
    • Modules is an array, and each item of the array is an object containing the create/update/destroy methods (or any of them). These methods are invoked during patch to realize corresponding capabilities, including creation, update and removal. This includes attrs, ref, directive, klass(class), style, and events creation, update, and removal.

Methods:

export function createPatchFunction (backend) {
  / /... There are a lot of internal tools and methods
  return function patch (oldVnode, vnode, hydrating, removeOnly) {
     // The return value of this function is the patch method, vm.__patch__}}Copy the code

Four,

Vm. _render and vm._update are called in the watcher evaluation call updateComponent method.

  1. Vm. $options.render VNode = vm.$options.render VNode = vm.$options.render VNode = vm.$options.render VNode = vm.$options.render VNode = vm.$options.render The so-called VNode is the legendary virtual DOM tree, describing the relationship between nodes;

  2. Vm. _update (vue.prototype. _update) takes the virtual DOM from the previous step, renders it to the page, and turns it into the real DOM, which is what the vm.__patch__ method does;

  3. Then we trace the process of vm.__patch__ (vue.prototype. __patch__) method, which is returned by createPatchFunction; Vm. __Patch__ is responsible for initial rendering and update rendering after responsive data update;