一般情况下每个vue组件都用"\<template\>"写html, 但实际还可以在js代码中通过render函数生成dom. 最主要常见组件库也需要配合"h"使用.
render
render是组件的一个选项, 他的返回值会被作为组件的DOM结构.
<script>
import { defineComponent} from "vue";
export default defineComponent({
render(){
return '123456789'
}
});
</script>
试试插入html:
<script>
import { defineComponent } from "vue";
export default defineComponent({
render(){
return '<h2>123456789</h2>'
}
});
</script>
可以看到html标签被当做字符串渲染了, 并没有生成h2标签. 如何正确插入h2标签呢?
VNode
如果想插入DOM就要用到"VNode", VNode是vue对页面DOM节点的描述, 其是一个Object类型.
h
结构这么复杂的"VNode"肯定不是自己拼写的, 官方提供了"h"函数, 可以帮助我们生成"VNode"
<script>
import { defineComponent, h } from "vue";
export default defineComponent({
render() {
const props = { style: { color: "red" } };
return h("h2", props, "123456789");
},
});
</script>
这次生成了真正"h2":
"h"函数的第1个参数是"标签名", 第2个是"属性", 在这个例子里可以理解为html的所有属性, 第3个是"内容". "内容"不仅仅可以是字符串, 还可以是"VNode"或2者混合:
<script>
import { defineComponent, h } from "vue";
export default defineComponent({
render() {
const props = { style: { color: "red" } };
const small = h("small", "副标题");
return h("h2", props, ["123456789", small]);
},
});
</script>
如果实际只传入2个参数, 那么第二2参数就会作为内容, 比如这里的"small".
渲染组件
"h"还可以渲染"组件", 这一下灵活度就上来了, 假设我们有一个"switch"组件, 其支持<switch v-model:checked="checked"/>
.
<script>
import { defineComponent, h } from "vue";
import ASwitch from "../components/ASwitch.vue";
export default defineComponent({
components: { ASwitch },
data() {
return { checked: false };
},
render() {
return h(ASwitch)
}
});
</script>
这里注意第1个参数还支持传入组件对象.
效果如下:
但是你可以发现了, "switch"虽然显示了, 但是点击后按钮并不能切换.
h函数中使用"v-model"
上面不能切换是因为没有像在模板中那样使用"v-model".
<a-switch v-model:checked="checked"></a-switch>
回忆下前面讲过的"自定义双向数据绑定"课中讲的如何实现"v-model", 对比下可以发现上面"h"中,没有定义"props"和"v-on事件监听", 怎么写呢?
先说一个重要的知识点: 组件上的事件监听其实也可通过"props"传入:
<a-switch @update:checked="onChange"></a-switch>
<!-- 等价写法: -->
<a-switch :onUpdate:checked="onChange"></a-switch>
所有的自定义事件, 都可以通过":on"前缀通过props传入. 所以在"h"中可以通过第2个参数传入"checked"属性和"onUpdate:checked"事件实现"v-model"的等同操作.
<script>
import { defineComponent, h } from "vue";
import ASwitch from "../components/ASwitch.vue";
export default defineComponent({
components: { ASwitch },
data() {
return { checked: false };
},
render() {
return h(ASwitch, {
checked: this.checked,
["onUpdate:checked"]: (checked) => {
this.checked = checked;
},
});
},
});
</script>
使用场景
实际开发中很多第三方组件都设计了可以接收"VNode"的"属性", 比如"ant-design-vue"的"Table"组件的"columns"属性中的"customRender"属性, 可以通过传入"VNode"实现样式自定义:
{
title: '状态',
customRender({ record }: any) {
if (1 === record.state) {
return h(Tag, { color: 'success' }, () => `开启`);
} else {
return h(Tag, { color: 'error' }, () => `关闭`);
}
},
},
代码中通过"h"渲染了"Tag"组件,效果如下:
总结
这节课讲了3个概念, 帮大家整理下他们3者的关系: "render"函数的返回值需要是"VNode"格式, "h"函数可以构造"VNode"格式数据.
🍕学习互动
感谢大家的阅读, 如有疑问可以加我微信, 我拉你进入微信群