前言
Vue中的render提供了一种以函数的方式写HTML的思路,比起template具有更强的可控性,也是一种更接近编译器的编写方式。在一些场景中,比如说开发组件库,包括数据模板渲染、组件按需加载、引入自定义组件插槽等等。这些时候我们往往需要用这种完全编程的方式来解决。本文提供render介绍和一些基本的实例。
一、虚拟DOM
谈到render函数就不得不先阐述清楚虚拟DOM:
人人都清楚DOM 是浏览器解析 HTML ,从而得来的一个树形逻辑对象。但如果用 Object 来代表一个节点,这个节点便叫做虚拟节点( Virtual Node 简写为 VNode ),由 VNode 树组成虚拟DOM。
使用虚拟DOM会有实际的好处:
Web 页面的大多数操作和逻辑的本质就是不停地修改 DOM 元素,但是 DOM 操作太慢了,过于频繁的 DOM 操作可能会导致整个页面掉帧、卡顿甚至失去响应。操纵DOM元素的代价是昂贵的。
仔细想一想,很多 DOM 操作是可以打包(多个操作压成一个)和合并(一个连续更新操作只保留最终结果)的,同时 JS 引擎的计算速度要快得多,所以为什么不把 DOM 操作先通过JS计算完成后统一来一次大招操作DOM呢,于是就有了虚拟DOM的概念。当然,虚拟DOM操作的核心是Diff算法,也就是比较变化前后Vnode的不同,计算出最小的DOM操作来改变DOM,提高性能。使用虚拟DOM往往是廉价的。
二、CreateElement函数
创建虚拟DOM的手段是使用CreateElement函数:
render: function (createElement) {
return createElement(App);
}
在vue中,可以使用 h 代替 createElement,然后以上代码可以通过使用 ES6 箭头函数缩短:
render: h => h (App)
vue版本最多需要三个参数:
render: h => {
return h('div', {}, [...])
}
第一种是元素的类型(以上显示为 div)。
第二个是数据对象。 我们在这里主要包括:props, attrs, dom props, class 和 style。
第三个是一组子节点。 然后,我们将嵌套调用并最终返回一个虚拟 DOM 节点树。
三、基础实例
以下是一个最基本的实例,包含了简单的渲染用法、标签、props、slot、点击事件:
<h1 class="wii-first">
第一个组件<slot></slot>
<slot name="subtitle"></slot>,此处是data的值: {{msg}}
<button @click="clickHandler">
点我改变内部data的值
</button>
</h1>
以上的代码用render函数的方式编写如下:
export default {
name: 'wii-first',
data() {
return {
msg: 0
}
},
props: {
level: {
type: [Number, String],
required: true
}
},
render: function(createElement) {
this.$slots.subtitle = this.$slots.subtitle || []
return createElement(
'h' + this.level, // tag name 标签名称,上面的代码假设此处是1
{
class: 'wii-first'
},
// this.$slots.default, // 子组件中的slot 单个传递
// this.$slots.subtitle,
[
'第一个组件, ',
...this.$slots.default, // 默认slots传递
...this.$slots.subtitle, // 具名slots传递
',此处是data的值: ',
this.msg,
createElement('button', {
on: {
click: this.clickHandler
},
}, '点我改变内部data值')
]
)
},
methods: {
clickHandler() {
this.msg = Math.ceil(Math.random() * 1000)
}
}
}
接下来把上面用render写好的Demo,再引入到组件中,如下:
<template>
<div id="app">
<wii-first level="1">我是标题 <span slot="subtitle">我是subtitle</span></wii-first>
</div>
</template>
<script>
import WiiFirst from './components/first/index.vue'
export default {
name: 'app',
components: {
WiiFirst
},
data() {
return {
}
}
}
</script>