In the previous chapter we saw that updateComponent actually calls vm._render to generate a VNode and vm._update to update the DOM.

Before we look at the _render function, let’s take a look at how to use the render function in Vue. The render function takes a createElement method as the first argument to create a VNode.

render: function (createElement) {
  return createElement('div', {
     attrs: {
        id: 'app'
      },
  }, this.message)
}
Copy the code


. The body begins to vm and _render defined in SRC/core/instance/render. The js file

Vue.prototype._render = function (): VNode {

  const vm: Component = this

  const { render. _parentVnode } = vm.$options



  if (_parentVnode) {

    // Slot-related logic

  }



  // Allow the render function to access placeholder vnode data

  vm.$vnode = _parentVnode



  let vnode

  try {

    // There is no need to maintain a stack, since all render functions are called separately from each other

    // The render function of nested components is called when the parent component patched

    currentRenderingInstance = vm

    // Core: call render function

    vnode = render.call(vm._renderProxy. vm.$createElement)

    

  } catch (e) {

    // Error handling...

    

  } finally {

    currentRenderingInstance = null

  }

  

  // Allow the returned array to contain only one node

  if (Array.isArray(vnode) && vnode.length = = = 1) {

    vnode = vnode[0]

  }

  

  // set parent

  vnode.parent = _parentVnode

  return vnode

}Copy the code

The core logic is to call the Render function, pass in the vm.$createElement parameter, bind this to vm._renderProxy, and return a VNode.

Let’s look at vm._renderProxy and vm.$createElement

1. vm._renderProxy

Vm. _renderProxy is a VM in production and a Proxy object in development. It is defined in the _init method.

// src/core/instance/init.js



Vue.prototype._init = function (options?: Object) {

  // ...

  if (process.env.NODE_ENV ! = = 'production') {

    initProxy(vm)

  } else {

    vm._renderProxy = vm

  }

  // ...

}Copy the code

Production environment with the initProxy function. InitProxy specifically defined in SRC/core/instance/proxy. Js

// src/core/instance/proxy.js



initProxy = function initProxy (vm) {

  // Check whether the browser supports proxy

  if (hasProxy) {

    _withStripped to decide which proxy handler to use

    const options = vm.$options

    const handlers = options.render && options.render._withStripped

      ? getHandler

      : hasHandler

    vm._renderProxy = new Proxy(vm. handlers)

  } else {

    vm._renderProxy = vm

  }

}Copy the code
  • First check whether the browser supports itProxy, if supported, judge_withStrippedattribute
    • _withStrippedIs true:Proxy handlerusinggetHandlermethods
    • _withStrippedFalse:Proxy handlerusinghasHandlerMethods.
  • If not, directlyvmAssigned to_renderProxy

1.1. getHandler

// src/core/instance/proxy.js



const getHandler = {

  get (target. key) {

    if (typeof key = = = 'string' && !(key in target)) {

      if (key in target.$data) warnReservedPrefix(target. key)

      else warnNonPresent(target. key)

    }

    return target[key]

  }

}Copy the code

Main Function When reading properties of a proxy object, a warning is thrown for different situations if the properties are not in the real VM instance

  • Case 1:keyNot invmIn, but invm.$dataTo show our custom data$_At first, this case is not to be data brokered, that is, directly throughthis.$xxAccess will getundefined(About data brokershereA)
  • Situation two: Novm, throws a warning

1.2. hasHandler

// src/core/instance/proxy.js



const hasHandler = {

  has (target. key) {

    const has = key in target

    const isAllowed = allowedGlobals(key) ||

      (typeof key = = = 'string' && key.charAt(0) = = = '_' && !(key in target.$data))

    if (!has && !isAllowed) {

      if (key in target.$data) warnReservedPrefix(target. key)

      else warnNonPresent(target. key)

    }

    return has || !isAllowed

  }

}Copy the code
  • The first to useinOperator to determine whether the property is invmExists on the instance.
  • Attribute is available if it is a special attribute (Number,Array, etc.) or a string that starts with an underscore and is not in $data.
  • If the property is invmDoes not exist, and the property is not available, throws a warning.
  • returnhas || ! isAllowed.! isAllowedIndicates that the property exists as a property if it is not available.

1.3. Why_withStrippedTo use hasHandler or getHandler?

This is actually to handle render functions in different cases

Before we do that, let’s make two things clear: when proxy.foo is accessed, proxy get is triggered, with(proxy) {(foo); } triggers the Proxy’s HAS. Look at MDN in detail

For non-single-file components, using EL or Templete to create the component, vue will parse the template to generate Render, as shown in figure 1

vm.$options.render = function () {
    with (this) {
        // where _c is vm._c, as described below
        return _c(/ *... * /)}}Copy the code

Our access to _c triggers the Proxy’s HAS, which is the hasHandler above

For SFC, the vue-loader compiles template to code that does not contain with in strict mode, but sets render._withStripped = true for time to view. Render looks like this after compiling:

var render = function() {
    var _vm = this;
    var _h = _vm.$createElement;
    var _c = _vm._self._c || _h;
    return _c(...)
}
Copy the code

When we access the property as _vm.xx, the Proxy get, or getHandler, is triggered

2. vm.$createElement

Render function in the createElement method is vm $createElement method method, defined in SRC/core/instance/render. Js

// src/core/instance/render.js



export function initRender (vm: Component) {

  // ...

  

  vm._c = (a. b. c. d) = > createElement(vm. a. b. c. d. false)

  vm.$createElement = (a. b. c. d) = > createElement(vm. a. b. c. d. true)



  // ...

}Copy the code
  • vm._cIs an internal function that is compiled by a templaterenderFunction USES
  • vm.$createElementIs provided for user authoringrenderFunction used

Both of them actually execute the createElement function, which is covered in a separate section

conclusion

Vm. _render finally returns vNode by executing the createElement method