Skip to content

4 - 数据驱动(vm._render) #4

@chenfaxiang

Description

@chenfaxiang

vm._render

_render 方法是在文件 /src/core/instance/render.js 中定义的一个方法,它用来把实例渲染成一个虚拟 Node,看一下代码(有点儿复杂,我要慢慢读。。。):

Vue.prototype._render = function (): VNode {
    const vm: Component = this
    // 取出配置项值
    const { render, _parentVnode } = vm.$options

    // reset _rendered flag on slots for duplicate slot check
    if (process.env.NODE_ENV !== 'production') {
      for (const key in vm.$slots) {
        // $flow-disable-line
        vm.$slots[key]._rendered = false
      }
    }

    if (_parentVnode) {
      vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject
    }

    // set parent vnode. this allows render functions to have access
    // to the data on the placeholder node.
    vm.$vnode = _parentVnode
    // render self
    let vnode
    try {
      // 调用 render 方法,生成虚拟 Node
      vnode = render.call(vm._renderProxy, vm.$createElement)
    } catch (e) {
      handleError(e, vm, `render`)
      // return error render result,
      // or previous vnode to prevent render error causing blank component
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production') {
        if (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
        }
      } else {
        vnode = vm._vnode
      }
    }
    // return empty vnode in case the render function errored out
    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
  }

这里主要是处理 render 操作,那么 render 函数的调用尤显重要,而在开发中手写 render 方法的场景少之又少,最多的还是直接用 template 模板;在之前的 mounted 方法学习中发现会把 template 编译成 render 方法,而 Vue 在官方的文档中介绍了 render 的第一个参数是 createElement ,即看下面的例子:

<div id="app">
  {{ message }}
</div>

相当于编写的 render 函数如下:

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

再回到当前文件 _render 方法中的 render 调用:

// 调用 render 方法,生成虚拟 Node
vnode = render.call(vm._renderProxy, vm.$createElement)

这里的 vm.$createElement 就是 render 函数中的 createElement 方法;而 vm.$createElement 方法定义是在 initRender 中,如下:

export function initRender (vm: Component) {
    ** other code **

  // bind the createElement fn to this instance
  // so that we get proper render context inside it.
  // args order: tag, data, children, normalizationType, alwaysNormalize
  // internal version is used by render functions compiled from templates

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

  // normalization is always applied for the public version, used in
  // user-written render functions.

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

  ** other code **
}

initRender 除了 vm.$createElement 方法外还有一个 vm._c 方法,而它是被模板编译成的 render 函数使用,vm.$createElement 是用户手写的 render 方法使用,这两个方法不仅支持的参数相同,且内部都调用了 createElement 方法;前面就是对 vm._render 的基本认识,而本节出现最多的就是虚拟dom vnode,那什么是虚拟dom,下面来一波 Virtual DOM。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions