render的方法解析——Vue源码解析

关于渲染函数的解释

不错鼓励并赞赏 标签: Javascript      评论 / 2019-09-15

_render的方法解析

Vue 的 _render 方法是实例的一个私有方法,它用来把实例渲染成一个虚拟 Node

首先_render方法是在初始化的时候文件src/core/instance/index.jsVue初始化的文件中定义的。

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

代码 1.1

其中代码 1.1中的renderMixin初始化对应的文件 src/core/instance/render.js文件中Vue.prototype._render方法是本次分析的重点

我们一起来看看这段代码:

Vue.prototype._render = function (): VNode {
    const vm: Component = this
    const {
      render,
      staticRenderFns,
      _parentVnode
    } = vm.$options

    if (vm._isMounted) {
      // if the parent didn't update, the slot nodes will be the ones from
      // last render. They need to be cloned to ensure "freshness" for this render.
      for (const key in vm.$slots) {
        const slot = vm.$slots[key]
        if (slot._rendered) {
          vm.$slots[key] = cloneVNodes(slot, true /* deep */)
        }
      }
    }

    vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) || emptyObject

    if (staticRenderFns && !vm._staticTrees) {
      vm._staticTrees = []
    }
    // 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 {
      vnode = render.call(vm._renderProxy, vm.$createElement)
    } catch (e) {
      handleError(e, vm, `render function`)
      // return error render result,
      // or previous vnode to prevent render error causing blank component
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production') {
        vnode = vm.$options.renderError
          ? vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
          : 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
  }

代码 1.2

我们可以看到很多关于$scopedSlots$slots$options等,后续会分析各自的意义。本次主要看代码片段1.3中

const {
      render,
      staticRenderFns,
      _parentVnode
    } = vm.$options

代码 1.3

其中render是从vm.$options中获取。具体vm.$options中如何定义,可以自行查看初始化的而过程。

之后代码会执行到vnode = render.call(vm._renderProxy, vm.$createElement)的片段

vm._renderProxy 是什么?

这是最为关键的两个参数,前面已经分析过了vm._renderProxy分为2个方式,生产环境下就是vm的意思,如果是开发环境则是 如下代码片段src/core/instance/init.js

 /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }

代码 1.4

可以看到initProxy就是在开发环境才会进入使用。然后可以进入代码片段分析src/core/instance/proxy.js中。最终主要就是在开发环境中,提前报出一些常见错误,基本都是 在这里报出的,比如模版参数不正确等。

vm.$createElement 是什么

我们可以看到render.js文件中的最开始的定义 方法initRender这个是在初始化的时候调用的。

可以看到预定义的几个静态变量

const parentVnode = vm.$vnode = vm.$options._parentVnode // the placeholder node in parent tree
const renderContext = parentVnode && parentVnode.context

代码 1.5

其实就是获取父节点和文本的对象,再往下查看代码

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)

代码 1.6

上面的两行代码都是使用了createElement的方法。

区别就在于,前者是给有编译环境下使用的render方法,后者是给手写render方法使用的

这篇主要介绍到这里。关于createElement参数可以查看官网createElemnt

总结

vm._render 最终是通过执行 createElement 方法并返回的是 vnode,它是一个虚拟 Node。Vue 2.0 相比 Vue 1.0 最大的升级就是利用了 Virtual DOM。因此在分析 createElement 的实现前,我们先了解一下 Virtual DOM 的概念

Hi 看这里!

大家好,我是PRO

我会陆续分享生活中的点点滴滴,当然不局限于技术。希望笔墨之中产生共鸣,每篇文章下面可以留言互动讨论。Tks bd!

博客分类

您可能感兴趣

作者推荐

呃,突然想说点啥

前端·博客

您的鼓励是我前进的动力---

使用微信扫描二维码完成支付