深入理解Vue组件之render函数

    一般来说,我们写 Vue.js 组件,模板都是写在 <template> 内的,但它并不是最终呈现的内容,template 只是一种对开发者友好的语法,能够一眼看到 DOM 节点,容易维护,在 Vue.js 编译阶段,会解析为 Virtual DOM。

    与 DOM 操作相比,Virtual DOM 是基于 JavaScript 计算的,所以开销会小很多。

    正常的 DOM 节点在 HTML 中是这样的:

<div id="main">
  <p>文本内容</p>
  <p>文本内容</p>
</div>

     用 Virtual DOM 创建的 JavaScript 对象一般会是这样的:

const vNode = {
  tag: 'div',
  attributes: {
    id: 'main'
  },
  children: [
    // p 节点
  ]
}

     vNode 对象通过一些特定的选项描述了真实的 DOM 结构。

     在 Vue.js 中,对于大部分场景,使用 template 足以应付,但如果想完全发挥 JavaScript 的编程能力,或在一些特定场景下,需要使用 Vue.js 的 Render 函数。

Render 函数

     正如上文介绍的 Virtual DOM 示例一样,Vue.js 的 Render 函数也是类似的语法,需要使用一些特定的选项,将 template 的内容改写成一个 JavaScript 对象。

     对于初级前端工程师,或想快速建站的需求,直接使用 Render 函数开发 Vue.js 组件是要比 template 困难的,原因在于 Render 函数返回的是一个 JS 对象,没有传统 DOM 的层级关系,配合上 if、else、for 等语句,将节点拆分成不同 JS 对象再组装,如果模板复杂,那一个 Render 函数是难读且难维护的。所以,绝大部分组件开发和业务开发,我们直接使用 template 语法就可以了,并不需要特意使用 Render 函数,那样只会增加负担,同时也放弃了 Vue.js 最大的优势(React 无 template 语法)。

       很多学习 Vue.js 的开发者在遇到 Render 函数时都有点”躲避“,或直接放弃这部分,这并没有问题,因为不用 Render 函数,照样可以写出优秀的 Vue.js 程序。不过,Render 函数并没有想象中的那么复杂,只是配置项特别多,一时难以记住,但归根到底,Render 函数只有 3 个参数。

       来看一组 template 和 Render 写法的对照:

<template>
  <div id="main" class="container" style="color: red">
    <p v-if="show">内容 1</p>
    <p v-else>内容 2</p>
  </div>
</template>
<script>
  export default {
    data () {
      return {
        show: false
      }
    }
  }
</script>
export default {
  data () {
    return {
      show: false
    }
  },
  render: (h) => {
    let childNode;
    if (this.show) {
      childNode = h('p', '内容 1');
    } else {
      childNode = h('p', '内容 2');
    }
    
    return h('div', {
      attrs: {
        id: 'main'
      },
      class: {
        container: true
      },
      style: {
        color: 'red'
      }
    }, [childNode]);
  }
}

  这里的 h,即 createElement,是 Render 函数的核心。可以看到,template 中的 v-if / v-else 等指令,都被 JS 的 if / else 替代了,那 v-for 自然也会被 for 语句替代。

h 有 3 个参数,分别是:

  1. 要渲染的元素或组件,可以是一个 html 标签、组件选项或一个函数(不常用),该参数为必填项。示例:

    // 1. html 标签
    h('div');
    // 2. 组件选项
    import DatePicker from '../component/date-picker.vue';
    h(DatePicker);
  2. 对应属性的数据对象,比如组件的 props、元素的 class、绑定的事件、slot、自定义指令等,该参数是可选的,上文所说的 Render 配置项多,指的就是这个参数。该参数的完整配置和示例,可以到 Vue.js 的文档查看,没必要全部记住。

  3. 子节点,可选,String 或 Array,它同样是一个 h。示例:

    [
      '内容',
      h('p', '内容'),
      h(Component, {
        props: {
          someProp: 'foo'
        }
      })
    ]
    render函数的约束

         所有的组件树中,如果 vNode 是组件或含有组件的 slot,那么 vNode 必须唯一。以下两个示例都是错误的。

    // 局部声明组件
    const Child = {
      render: (h) => {
        return h('p', 'text');
      }
    }
    
    export default {
      render: (h) => {
        // 创建一个子节点,使用组件 Child
        const ChildNode = h(Child);
        
        return h('div', [
          ChildNode,
          ChildNode
        ]);
      }
    }
    {
      render: (h) => {
        return h('div', [
          this.$slots.default,
          this.$slots.default
        ])
      }
    }

      重复渲染多个组件或元素,可以通过一个循环和工厂函数来解决:

    const Child = {
      render: (h) => {
        return h('p', 'text');
      }
    }
    
    export default {
      render: (h) => {
        const children = Array.apply(null, {
          length: 5
        }).map(() => {
          return h(Child);
        });
        return h('div', children);
      }
    }
    Render 函数使用场景

     上文说到,一般情况下是不推荐直接使用 Render 函数的,使用 template 足以,在 Vue.js 中,使用 Render 函数的场景,主要有以下 4 点:

  4. 使用两个相同 slot。在 template 中,Vue.js 不允许使用两个相同的 slot,比如下面的示例是错误的:

    <template>
      <div>
        <slot></slot>
        <slot></slot>
      </div>
    </template>

    可以使用一个深度克隆 VNode 节点的方法。

  5. 在 SSR 环境(服务端渲染),如果不是常规的 template 写法,比如通过 Vue.extend 和 new Vue 构造来生成的组件实例,是编译不过的,在 SSR 中会报错。此时需要使用render函数来解决。

  6. 在 runtime 版本的 Vue.js 中,如果使用 Vue.extend 手动构造一个实例,使用 template 选项是会报错的。解决方案也很简单,把 template 改写为 Render 就可以了。需要注意的是,在开发独立组件时,可以通过配置 Vue.js 版本来使 template 选项可用,但这是在自己的环境,无法保证使用者的 Vue.js 版本,所以对于提供给他人用的组件,是需要考虑兼容 runtime 版本和 SSR 环境的。

  7. 一个 Vue.js 组件,有一部分内容需要从父级传递来显示,如果是文本之类的,直接通过 props 就可以,如果这个内容带有样式或复杂一点的 html 结构,可以使用 v-html 指令来渲染,父级传递的仍然是一个 HTML Element 字符串,不过它仅仅是能解析正常的 html 节点且有 XSS 风险。当需要最大化程度自定义显示内容时,就需要 Render 函数,它可以渲染一个完整的 Vue.js 组件。你可能会说,用 slot 不就好了?的确,slot 的作用就是做内容分发的,但在一些特殊组件中,可能 slot 也不行。比如一个表格组件 Table,它只接收两个 props:列配置 columns 和行数据 data,不过某一列的单元格,不是只将数据显示出来那么简单,可能带有一些复杂的操作,这种场景只用 slot 是不行的,没办法确定是那一列的 slot。此时需要使用render函数就是一种解决方案。

结尾

         好了,以上就是关于VUE组件Vue组件之render函数,大家有什么疑问的欢迎在下面评论留言喔~

猜你喜欢

转载自blog.csdn.net/quxuetrip/article/details/131787143