博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Vue源码分析系列三:render
阅读量:7122 次
发布时间:2019-06-28

本文共 6043 字,大约阅读时间需要 20 分钟。

回顾

在上一系列中,我么已经了解到$mount是如何工作的,最后通过调用 updateComponent方法来执行vm._update(vm._render(), hydrating);,接下来我们来分析一下 vm._render()方法是如何运行的。


手写 render 方法

首先来看一下使用template的形式,

new Vue({  el: '#app',  template: '

hello world

'})// 或者这种方式new Vue({ el: '#app', components: { App }, template: '
'})复制代码

那么,我们如何手写 render 函数呢?仔细观察下面这段代码,试想一下这里的 createElement 参数是什么 。

new Vue({    el: '#app',    render(createElement) {        return createElement('div', {            attrs: {                id: 'app1' //注意这里的id 是 app1            }        }, this.value)    },    data() {        return {            value: 'render function'        }    }})复制代码

来看页面效果:

请注意上面红框框的 id, 是我们刚刚定义的 'app1'。所以我们为什么不能将元素绑定在body/html这些元素上就知道了吧,因为它会替换掉页面上的元素。

好了,进入正题,开始分析源码。

_render

Vue 的 _render 方法是实例的一个私有方法,它用来把实例渲染成一个虚拟 Node。它的定义在 src/core/instance/render.js 文件中:

注意,在分析源码的时候一定要走主线,不能一次性将某一个方法全看完或看透彻,因为它可能依赖了很多其它变量或者方法,如果都去分析,可能到后面看着看着就什么都看不懂了。在这里我们只分析 render 方法,像下面的 _parentVnode、$slots、$vnode 等等在后面的系列章节中再分析。

Flow 是 facebook 出品的 JavaScript 静态类型检查工具。Vue.js 的源码利用了 Flow 做了静态类型检查,function (): VNode表示这个方法的返回值是一个 vnode。

在这段代码中,主要研究这一段代码 render.call(vm._renderProxy, vm.$createElement)

Vue.prototype._render = function (): VNode {    const vm: Component = this    const { render, _parentVnode } = vm.$options    // 下面这两个 if 先不用看    // 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方法中的createElement参数就是 vm.$createElement方法      vnode = render.call(vm._renderProxy, vm.$createElement)            // 这个render 是 vm.$options.render      // vm._renderProxy 如果在生产环境下,其实就是 vm       // 如果在开发环境下,就是 Proxy 对象(ES6中的API,不了解的话可以去看看)      // vm.$createElement 定义在 initRender 函数中,初始化的时候定义的      // 手写 render 函数创建 vnode 的方法      // vm.$createElement = function (a, b, c, d) {       //    return createElement(vm, a, b, c, d, true);      // };            // 如果是编译生成的render函数,创建vnode的方法则是下面这个方法      // vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };          } 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  }}复制代码

_renderProxy 是什么 ?

这个方法在初始化的时候就定义好了,在 initMixin中的 Vue.prototype._init() 方法中,我们来看一下是如何定义的:

if (process.env.NODE_ENV !== 'production') {  // 如果是开发环境,就调用 initProxy 方法  initProxy(vm);} else {  // 否则就是 vm 实例  vm._renderProxy = vm;}复制代码

Proxy对象

描述: Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

  • 详情

initProxy 方法

路径: src/core/instance/proxy.js

// proxy.js中还有一些其它方法,这里我们只看 initProxy方法let initProxyif (process.env.NODE_ENV !== 'production') {  // 判断我们的浏览器支不支持proxy方法  const hasProxy =    typeof Proxy !== 'undefined' && isNative(Proxy)    if (hasProxy) {        const isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact')        config.keyCodes = new Proxy(config.keyCodes, {          set (target, key, value) {            if (isBuiltInModifier(key)) {              warn(`Avoid overwriting built-in modifier in config.keyCodes: .${key}`)              return false            } else {              target[key] = value              return true            }          }        })    }    var hasHandler = {        has: function has (target, key) {          var has = key in target;          var isAllowed = allowedGlobals(key) || key.charAt(0) === '_';          if (!has && !isAllowed) {            // 这个警告就是当你使用了没有定义的变量或者方法时调用的方法            // 方法就不列出来了。            warnNonPresent(target, key);          }          return has || !isAllowed        }    };    initProxy = function initProxy (vm) {        if (hasProxy) {          // determine which proxy handler to use          const options = vm.$options          const handlers = options.render && options.render._withStripped            ? getHandler            : hasHandler          // options.render上没有_withStripped,所以handlers 就是hasHandler          vm._renderProxy = new Proxy(vm, handlers)        } else {          vm._renderProxy = vm        }    }}export { initProxy }复制代码

最后

当执行完了 _initProxy 之后,再回到 render 方法中看如下这段代码

if (!(vnode instanceof VNode)) {      if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {        // render function 返回了多个 vnode 根节点        // 应当返回单一 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    return vnode复制代码

总结

vm._render 最终是通过执行 createElement 方法并返回的是 vnode,那这个 createElement 是如何创建 vnode 的呢 ? 在后面的两个系列中,我会先介绍 virtual DOM 的概念,然后再分析 createElement 的实现。

转载地址:http://eesel.baihongyu.com/

你可能感兴趣的文章
linux每日命令(1):which
查看>>
Bolt XML和JQBolt Lua代码自动补全插件配置教程
查看>>
我的友情链接
查看>>
剑指offer:调整数组顺序使奇数位于偶数前面
查看>>
mysql参数优化和硬件优化等分享
查看>>
Create superuser in Django
查看>>
Mysql主从复制配置
查看>>
rsync服务器
查看>>
使用Hexo+Github一步步搭建属于自己的博客(基础)
查看>>
数据库优化资料整理
查看>>
tomcat 原理与使用资料
查看>>
报此错错解决办法:java.lang.NoSuchMethodError: javax.persistence.OneToMany.orphanRemoval()Z
查看>>
c#.net利用RNGCryptoServiceProvider产生任意范围强随机数的办法
查看>>
旅游网站进行邮件订阅的七大步骤讲解
查看>>
shell基础
查看>>
linux 文件类型 时间戳 ls bash特性四 文件查看命令 cp move echo
查看>>
如何在XenDesktop中映射USB设备
查看>>
Java并发编程 基础知识学习总结
查看>>
Amdahl定律以及该定律在多核时代的影响
查看>>
我又发现一个直接就能安装中文小红帽的方法
查看>>