Vue绝大多数情况下推荐使用 template 来创建你的HTML,然而在一些特性的情况下, 你真的还是需要 js 完全的编程能力。就是 render 函数,他比template 更加接近编译器。
<h1>
<a name="hello-world" href="#hello-world">
Hello world!
</a>
</h1>
在html层,我们决定这样定义组件的接口:
<anchored-heading :level="1">Hello world!</anchored-heading>
当我们开始写一个通过 level prop 动态生成 heading 标签的组件,你可能很快想到这样实现(也就是根据level的值的不同动态生成不同的组件):
<script type="text/x-template" id="anchored-heading-template">
<h1 v-if="level === 1">
<slot></slot>
</h1>
<h2 v-else-if="level === 2">
<slot></slot>
</h2>
<h3 v-else-if="level === 3">
<slot></slot>
</h3>
<h4 v-else-if="level === 4">
<slot></slot>
</h4>
<h5 v-else-if="level === 5">
<slot></slot>
</h5>
<h6 v-else-if="level === 6">
<slot></slot>
</h6>
</script>
Vue.component('anchored-heading', {
template: '#anchored-heading-template',
props: {
level: {
type: Number,
required: true
}
}
})
在这种情况下 使用 template
并不是最好的选择,首先代码冗长,为了在不同的情况下插入内容,我们使用了很多的 slot
虽然模版在大多数的组件中都非常好用,但是在这种情况下就不是很简洁了,那么我们现在尝试使用 render 函数来重写上面例子。
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement(
'h' + this.level, // tag name 标签名称
this.$slots.default // 子组件中的阵列
)
},
props: {
level: {
type: Number,
required: true
}
}
})
简单清晰很多!简单来说,这样代码精简很多,但是需要非常熟悉 Vue 的实例属性。在这个例子中,你需要知道当你不使用 slot
属性向组件中传递内容时,比如 anchored-heading
中的 Hello world!
,这些子元素被存储在组件实例中的 $slots.default
中。如果你还不了解,** 在深入 render 函数之前推荐阅读 实例属性 API。**
节点、树、以及虚拟DOM
在深入渲染函数之前。了解浏览器的一些工作原理是非常重要的。
<div>
<h1>My title</h1>
Some text content
<!-- TODO: Add tagline -->
</div>
当浏览器读到这些代码时,它会建立一个“DOM 节点”树来保持追踪,如同你会画一张家谱树来追踪家庭成员的发展一样。
html和dom的节点树如下:
顺序是从外到内的。
每个元素都是一个节点,每片元素也是一个节点,甚至注释也是我们的节点,一个节点就是页面的一部分。就像家谱树一样,节点中可能还会包含子节点。
但是高效的更新所有这些节点 是比较困难的。不过所幸的是我们不需要在手动完成这些工作了。只需要告诉vue我们所希望的html是什么。这可以在一个模版中:
<h1>{{ blogTitle }}</h1>
也可以在一个函数中:
render: function (createElement) {
return createElement('h1', this.blogTitle)
}
在这两种情况下,Vue 都会自动保持页面的更新,即便 blogTitle 发生了改变。