Vue源码解析系列——数据驱动篇:render的执行过程

准备

vue版本号2.6.12,为方便分析,选择了runtime+compiler版本。

回顾

如果有感兴趣的同学可以看看我之前的源码分析文章,这里呈上链接:《Vue源码分析系列:目录》

_render的定义

Vue原型上_render定义在core/instance/render.js,这个文件随着core/instance/index.jsrenderMixin(Vue)的调用而加载。
以下是render.js文件。(剔除了非主线分代码,下同)

/** core/instance/render.js **/

//在Vue原型上挂载私有的_render方法
  Vue.prototype._render = function (): VNode {
  //从options中获取用户自己定义的render方法
    const { render, _parentVnode } = vm.$options;
    let vnode;
    try {
      currentRenderingInstance = vm;
      /**
       * 调用用户自己定义的render方法,传入 vm.__renderProxy,vm.$createElement,获得一个vnode
       * __renderProxy:生产环境为vm,也就是this,开发环境为一个Proxy对象(见"./init.js")
       * $createElement:见同文件的 initRender() 函数
       */
      vnode = render.call(vm._renderProxy, vm.$createElement)
     }
  }

这段代码主要是调用了用户传入的render函数,传入了两个参数,一个是_renderProxy,另一个是一个$createElement()方法,这个函数返回了一个VNode类型的值。我们先来看下_renderProxy

_renderProxy

_renderProxy定义在core/instance/init.js
这里做了一个判断:

  • 如果是开发环境的Vue,就执行一个initProxy的函数,传入Vue实例。
  • 如果是生产环境的Vue,_renderProxy就是Vue实例this
/** core/instance/init.js **/

/**
 * 如果是开发环境,_renderProxy是一个代理
 * 如果是生产环境,_renderProxy就是this
 */
if (process.env.NODE_ENV !== "production") {
  initProxy(vm);
} else {
  vm._renderProxy = vm;
}

initProxy

我们进入initProxy看一下究竟这个_renderProxy是个啥?
initProxy定义在core/instance/proxy.js
这边其实是对Vue的实例vm也就是this做了一个数据劫持proxy
需要一些es6的proxy的知识。

/** core/instance/proxy.js **/

/**
 * 开发环境下初始化vm._renderProxy
 * 对对象访问进行劫持
 */
initProxy = function initProxy(vm) {
  /**
   * 是否兼容es6的proxy
   */
  if (hasProxy) {
    // determine 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;
  }
};

这里的proxy定义了handlers,我们去看看getHandler

/** 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];
  },
};

好像是对数据的get操作做了一些判断。我们去看看warnNonPresent

/** core/instance/proxy.js **/

onst warnNonPresent = (target, key) => {
  warn(
    `Property or method "${key}" is not defined on the instance but ` +
      "referenced during render. Make sure that this property is reactive, " +
      "either in the data option, or for class-based components, by " +
      "initializing the property. " +
      "See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.",
    target
  );
};

啊这,这不是……我们经常会报的错误吗?这里什么意思我就不用说了吧?
这下我们差不多搞懂了_renderProxy()了。该回到_render()了去看看$createElement()

$createElement

那这个$createElement又是何方神圣呢?这个$createElement其实就是我们手写render(h){ ... }函数所需要的参数h,在render.js文件的顶部可以找到相关的定义。
这里有两个调用,一个是$createElement,另外一个vm._c是个Vue内部自身调用的(用于模板编译后的render())。
我们这里的render不是template编译后的render,是用户自己手写的render,所以我们接下来要用vm.$createElement
vm.$createElement调用了一个createElement()的函数,由于这个函数也是一个需要详细解读的部分,具体放在下一篇createElement的执行过程中讲解。

/** core/instance/render.js **/
export function initRender(vm: Component) {
//template编译出的render
  vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false);
//用户自定义的render
  vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true);
}

好,我们回到_render():
这里调用$createElement后得到一个VNode,然后对这个VNode做了两波判断,最后直接return vnode;

/** core/instance/render.js **/
vnode = render.call(vm._renderProxy, vm.$createElement);
//如果vnode是一个数组且长度为1,赋值数组的第一个元素给vnode
if (Array.isArray(vnode) && vnode.length === 1) {
  vnode = vnode[0];
}
//当vnode不是一个VNode类型且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给vnode
  vnode = createEmptyVNode();
}
return vnode;

至此,render的执行过程阅读完毕

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱学习的前端小黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值