vue-render函数

扫码查看

今天我们来看一下,vue的render函数。

说明: 
我们接着来,我们今天先来粗略的了解下render函数,后面我会用大量的代码进行说明,便于大家先简单的了解,后边我们在进行深入了解。

基础

什么是render函数呢?Vue官方给我们已经说明。 
VUE推荐在绝大多数情况下使用template来创建我们的HTML。然而在一些场景中,我们真的需要JavaScript的完全编程的能力,这就是render函数,它比template更接近编译器。

  1.  
    <h1>
  2.  
    <a name="hello-world" href="#hello-world">
  3.  
    Hello world!
  4.  
    </a>
  5.  
    </h1>

在HTML层,我们决定这样定义组件接口:

<anchored-heading :level="1">Hello world!</anchored-heading>

当我们开始写一个通过levelprop 动态生成heading 标签的组件,你可能很快想到这样实现:

  1.  
    <script type="text/x-template" id="anchored-heading-template">
  2.  
    <h1 v-if="level === 1">
  3.  
    <slot></slot>
  4.  
    </h1>
  5.  
    <h2 v-else-if="level === 2">
  6.  
    <slot></slot>
  7.  
    </h2>
  8.  
    <h3 v-else-if="level === 3">
  9.  
    <slot></slot>
  10.  
    </h3>
  11.  
    <h4 v-else-if="level === 4">
  12.  
    <slot></slot>
  13.  
    </h4>
  14.  
    <h5 v-else-if="level === 5">
  15.  
    <slot></slot>
  16.  
    </h5>
  17.  
    <h6 v-else-if="level === 6">
  18.  
    <slot></slot>
  19.  
    </h6>
  20.  
    </script>
  1.  
    Vue.component('anchored-heading', {
  2.  
    template: '#anchored-heading-template',
  3.  
    props: {
  4.  
    level: {
  5.  
    type: Number,
  6.  
    required: true
  7.  
    }
  8.  
    }
  9.  
    })
  •  

在这种场景中使用 template 并不是最好的选择:首先代码冗长,为了在不同级别的标题中插入锚点元素,我们需要重复地使用<slot></slot>

虽然模板在大多数组件中都非常好用,但是在这里它就不是很简洁的了。那么,我们来尝试使用render函数重写上面的例子:

  1.  
    Vue.component('anchored-heading', {
  2.  
    render: function (createElement) {
  3.  
    return createElement(
  4.  
    'h' + this.level, // tag name 标签名称
  5.  
    this.$slots.default // 子组件中的阵列
  6.  
    )
  7.  
    },
  8.  
    props: {
  9.  
    level: {
  10.  
    type: Number,
  11.  
    required: true
  12.  
    }
  13.  
    }
  14.  
    })
  •  

简单清晰很多!简单来说,这样代码精简很多,但是需要非常熟悉 Vue 的实例属性。在这个例子中,你需要知道当你不使用slot 属性向组件中传递内容时,比如 anchored-heading 中的 Hello world!, 这些子元素被存储在组件实例中的 $slots.default中。如果你还不了解, 在深入 render 函数之前推荐阅读实例属性API。

我们来一个生成模板对比,便于学习 
非使用render方法的情况下

  1.  
    <div id="app">
  2.  
    <child :level="1">Hello world!</child>
  3.  
    </div>
  4.  
    <script type="text/x-template" id="template">
  5.  
    <div>
  6.  
    <h1 v-if="level === 1">
  7.  
    <slot></slot>
  8.  
    </h1>
  9.  
     
  10.  
    <h2 v-if="level === 2">
  11.  
    <slot></slot>
  12.  
    </h2>
  13.  
     
  14.  
    <h3 v-if="level === 3">
  15.  
    <slot></slot>
  16.  
    </h3>
  17.  
     
  18.  
    <h4 v-if="level === 4">
  19.  
    <slot></slot>
  20.  
    </h4>
  21.  
     
  22.  
    <h5 v-if="level === 5">
  23.  
    <slot></slot>
  24.  
    </h5>
  25.  
     
  26.  
    <h6 v-if="level === 6">
  27.  
    <slot></slot>
  28.  
    </h6>
  29.  
    </div>
  30.  
    </script>
  31.  
     
  32.  
     
  33.  
     
  34.  
    <script type="text/javascript">
  35.  
     
  36.  
    Vue.component('child', {
  37.  
    template: '#template',
  38.  
    props: {
  39.  
    level: {
  40.  
    type: Number,
  41.  
    required: true
  42.  
    }
  43.  
    }
  44.  
    })
  45.  
     
  46.  
    new Vue({
  47.  
    el: "#app"
  48.  
    })
  49.  
     
  50.  
    </script>
  •  

这样的代码有一个缺陷,就是需要大量重复的代码。如<h1>~<h6>共六种情况。如果这样的模板比较复杂,比如<hx>标签里还有很多其他内容,显然会让人十分困扰,既不美观又不好用。 
使用render方法的情况

  1.  
    <div id="app">
  2.  
    <child v-bind:level="2">Hello world!</child>
  3.  
    </div>
  4.  
     
  5.  
    <script type="text/javascript">
  6.  
     
  7.  
    Vue.component('child', {
  8.  
    render: function (createElement) {
  9.  
    return createElement(
  10.  
    'h' + this.level, // tag name 标签名称
  11.  
    this.$slots.default // 子组件中的阵列
  12.  
    )
  13.  
    },
  14.  
    props: {
  15.  
    level: {
  16.  
    type: Number,
  17.  
    required: true
  18.  
    }
  19.  
    }
  20.  
    })
  21.  
     
  22.  
    new Vue({
  23.  
    el: "#app"
  24.  
    })
  25.  
     
  26.  
    </script>
  •  

比如说,有需要分发的标签 <slot>,则通过 this.$slots.default 来获得,或许还有其他组件之类,可能需要被使用的,应该也是放在这里。 
个人初步理解是,在原本被替换的地方,例如例上面代码中的 <child> 标签,在其下的每个次一级标签为一个元素,放在 this.$slots.default这个数组中,例如上面的话,这个数组只有一个元素,但是以下代码: 

<child v-bind:level="2"><div>Hello</div><div> world!</div></child> 

这个数组中则有两个元素,分别是 <div>Hello</div> 和 <div> world!</div>例如 this.$slots.default[0] 则表示第一个标签。

另外,假如这2个元素中间有空格、换行符之类,那么数组中则有三个元素,那个空格、换行符则为第二个元素。这个属性有点类似dom的 childNodes属性,不是单纯以标签才算作子节点的。(但注意,不是完全相同)

有时候,我们可能想在里面添加其他的组件,例如将abc组件注册在里面。那么,我们首先要注册这个abc组件,然后将abc组件在数组里通过createElement来创建标签,只有通过这样的方式创建的abc标签,才能被abc组件使用。单纯输入字符串"<abc></abc>" 这样是不可以的。

如以下代码也是可行的(在通过render方法生成的模板中添加别的组件):

  1.  
    <div id="app">
  2.  
    <child v-bind:level="2">
  3.  
    <div>Hello</div>
  4.  
    <div> world!</div>
  5.  
    </child>
  6.  
    </div>
  7.  
     
  8.  
    <script>
  9.  
     
  10.  
    Vue.component('abc', {
  11.  
    template: "<div>abc</div>"
  12.  
    })
  13.  
     
  14.  
    Vue.component('child', {
  15.  
    render: function (createElement) {
  16.  
    console.log(this.$slots)
  17.  
    return createElement(
  18.  
    'h' + this.level, // tag name 标签名称
  19.  
    [this.$slots.default[0], createElement("abc"), this.$slots.default[1]] // 子组件中的阵列
  20.  
    )
  21.  
    },
  22.  
    props: {
  23.  
    level: {
  24.  
    type: Number,
  25.  
    required: true
  26.  
    }
  27.  
    }
  28.  
    })
  29.  
     
  30.  
    new Vue({
  31.  
    el: "#app"
  32.  
    })
  33.  
     
  34.  
    </script>
  •  

显示结果是:

Hello

abc

world!

最后,如果想将原有内容全部使用(而不是只取部分标签),那么直接使用 this.$slots.default 作为第三个参数即可,他本身就是一个数组。

总而言之,createElement方法的作用就是动态的创建一个dom用于被render函数渲染,其中参数二和参数三可以选择性省略,参数二用于设置dom的样式、属性、事件等,参数三用于设置分发的内容,包括新增的其他组件。 
粗略理解的话,可以理解为:createElement( 标签名, 标签属性, 标签里的内容)。

这样的话,我们就有了一个组件的模板所需要的全部内容了。后边我们在深入的了解下render函数。

02-01 05:07
查看更多