大家好,我是 前端架构师 - 大卫。
更多优质内容请关注微信公众号 @程序员大卫。
初心为助前端人🚀,进阶路上共星辰✨,
您的点赞👍与关注❤️,是我笔耕不辍的灯💡。
背景
上一篇文章我们讲了 h
函数到底是什么,以及它的基本用法。这一篇将继续深入,介绍 h
函数在实际开发中的几种典型运用场景。
三种运用场景
1. 二次组件的封装
假设我们在 App.vue
中调用 HelloWorld
组件,并传递了插槽:
html
<!-- App.vue -->
<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
</script>
<template>
<HelloWorld>
<template #header>
<div>Header</div>
</template>
<div>Main</div>
<template #footer>
<div>Footer</div>
</template>
</HelloWorld>
</template>
HelloWorld.vue
组件如下:
html
<!-- HelloWorld.vue -->
<template>
<div>
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</div>
</template>
页面最终渲染内容为:
css
Header
Main
Footer
现在如果我们想对 HelloWorld
进行二次封装,可以创建一个 Child.vue
组件:
html
<!-- Child.vue -->
<script setup lang="ts">
import HelloWorld from "./HelloWorld.vue";
</script>
<template>
<div>
<h1>Child</h1>
<HelloWorld v-bind="$attrs">
<template v-for="(_, slot) in $slots" #[slot]="slotProps">
<slot :name="slot" v-bind="slotProps"></slot>
</template>
</HelloWorld>
</div>
</template>
此时我们在 App.vue
中改为调用 Child.vue
:
html
<!-- App.vue -->
<script setup lang="ts">
import Child from "./components/Child.vue";
</script>
<template>
<Child>
<template #header>
<div>Header</div>
</template>
<div>Main</div>
<template #footer>
<div>Footer</div>
</template>
</Child>
</template>
最终页面渲染:
css
Child
Header
Main
Footer
这种方式虽然可以实现插槽透传,但写法略显繁琐。我们可以使用 <component>
动态组件结合 h
函数简化写法:
html
<!-- Child.vue -->
<script setup lang="ts">
import { h } from "vue";
import HelloWorld from "./HelloWorld.vue";
</script>
<template>
<div>
<h1>Child</h1>
<component :is="h(HelloWorld, $attrs, $slots)"></component>
</div>
</template>
2. 命令式地显示弹框
假如我们项目中使用了 ant-design-vue
组件库,展示弹框通常是这样的:
html
<!-- App.vue -->
<template>
<a-button type="primary" @click="showModal">Open Modal</a-button>
<a-modal v-model:open="open" title="Basic Modal" @ok="handleOk">
<p>Some contents...</p>
</a-modal>
</template>
<script lang="ts" setup>
import { ref } from "vue";
const open = ref(false);
const showModal = () => {
open.value = true;
};
const handleOk = () => {
open.value = false;
};
</script>
我们可以通过 h
函数来命令式地展示弹框:
html
<!-- App.vue -->
<template>
<a-button type="primary" @click="showModal">Open Modal</a-button>
</template>
<script lang="ts" setup>
import { Modal } from "ant-design-vue";
import { createApp, h } from "vue";
const showModal = () => {
const modal = () =>
h(
Modal,
{
title: "Basic Modal",
open: true,
onOk: () => unmount(),
onCancel: () => unmount(),
},
() => h("p", "Some contents...")
);
const div = document.createElement("div");
document.body.appendChild(div);
const app = createApp(modal);
app.mount(div);
const unmount = () => {
app.unmount();
document.body.removeChild(div);
};
};
</script>
⚠️ 这种方式的小问题是:弹框关闭时不会有动画过渡效果。
3. 表格中动态渲染内容
在使用 ant-design-vue
渲染表格时,基础代码如下:
html
<template>
<a-table :columns="columns" :data-source="data" bordered></a-table>
</template>
<script lang="ts" setup>
const columns = [
{
title: "Name",
dataIndex: "name",
},
{
title: "Address",
dataIndex: "address",
},
];
const data = [
{ name: "John Brown", address: "New York No. 1 Lake Park" },
{ name: "Jim Green", address: "London No. 1 Lake Park" },
{ name: "Joe Black", address: "Sidney No. 1 Lake Park" },
];
</script>
如果我们希望为"名字"加上超链接,可以通过插槽实现:
html
<template>
<a-table :columns="columns" :data-source="data" bordered>
<template #bodyCell="{ column, text }">
<template v-if="column.dataIndex === 'name'">
<a href="#">{{ text }}</a>
</template>
</template>
</a-table>
</template>
<script lang="ts" setup>
// columns 和 data 同上
</script>
还可以通过 customRender
配合 h
函数实现更灵活的渲染逻辑:
html
<template>
<a-table :columns="columns" :data-source="data" bordered></a-table>
</template>
<script lang="ts" setup>
import { h } from "vue";
const columns = [
{
title: "Name",
dataIndex: "name",
customRender: ({ text }: { text: string }) => {
return h("a", { href: "#" }, text);
},
},
{
title: "Address",
dataIndex: "address",
},
];
// data 同上
</script>
如果内容更复杂,也可以使用 tsx
写法进一步提升可读性和可维护性。
总结
通过这篇文章我们可以看到,h
函数在 Vue 3 中非常强大,适用于:
- 插槽透传时简化代码;
- 实现命令式弹框;
- 动态渲染表格内容等高级场景。
掌握 h
函数的使用,有助于我们编写更灵活、更底层、更可控的 Vue 组件逻辑。