Appearance
h 函数定义和使用插槽
使用 h 函数用来创建渲染虚拟 DOM,可以封装成函数,可以通过 JS 中调用快速渲染出一个组件。可是 h 函数如何定义和使用插槽呢?
1.定义插槽
使用renderSlots
来定义插槽,其原理就是通过上下文中的 slots 对象获取某个具名插槽的 key 来渲染出该插槽的内容
js
const components = {
setup(_, { slots }) {
return () => {
h("div", null, [
renderSlots(slots, "default"), // 默认插槽
renderSlots(slots, "footer"), // 具名插槽,名为:footer
renderSlots(slots, "header"), // 具名插槽,名为:header
]);
};
},
};
const components = {
setup(_, { slots }) {
return () => {
h("div", null, [
renderSlots(slots, "default"), // 默认插槽
renderSlots(slots, "footer"), // 具名插槽,名为:footer
renderSlots(slots, "header"), // 具名插槽,名为:header
]);
};
},
};
也可以获取到对应渲染函数直接调用函数一样可以。
js
const components={
setup(_,{slots}){
return ()=>{
h('div',null,[
h('div',slots?.default()),
slots?.footer()
slots?.header()
])
}
}
}
const components={
setup(_,{slots}){
return ()=>{
h('div',null,[
h('div',slots?.default()),
slots?.footer()
slots?.header()
])
}
}
}
2.使用插槽
js
h(
components,
null, // 这一项必须为null,否则会插槽会被认为是props、attrs
{
default: () => h("div", "hello~"),
footer: () => h("div", "hello~"),
header: () => h("div", "hello~"),
}
);
h(
components,
null, // 这一项必须为null,否则会插槽会被认为是props、attrs
{
default: () => h("div", "hello~"),
footer: () => h("div", "hello~"),
header: () => h("div", "hello~"),
}
);
3.特殊情况
如果说我们通过 h 函数渲染的组件有第三方插件例如i18n
会在模板中使用$t
来渲染国际化文本就会导致因为虚拟 DOM 无$t
从而渲染失败而报错。解决方案:
1.新建 App 实例(不推荐)
我们可以通过 createApp 新建一个 app 实例,并通过 app.use 按照插件,再通过 app 组件的 render 函数来渲染组件。
但是这样多多少少都会有内容开销,毕竟一个 app 实例+插件是是否繁重的。
下面是一个模态框的封装:
ts
import { createApp, h, render, renderSlot } from "vue";
import type { Component } from "vue";
import "./index.css";
import i18n from "@/config/i18n";
export function renderModal(content: Component, props: Record<any, any>) {
// 遮罩层
const mask = document.createElement("div");
mask.classList.add("modal-mask-container");
const app = createApp({
name: "ModalApp",
render() {
return container;
},
});
// 实际组件(模态框)
const container = h(
// 定义
{
name: "ModalContainer",
setup(_, context) {
return () =>
h(
"div",
{
class: "modal-container",
},
[renderSlot(context.slots, "default")]
);
},
},
null,
// 使用
{
default: () =>
h(content, {
...props,
onClose() {
console.log("关闭事件触发了!");
},
}),
}
);
// 按照插件
app.use(i18n);
// 将虚拟DOM渲染再真实DOM中
app.mount(mask);
document.body.appendChild(mask);
}
import { createApp, h, render, renderSlot } from "vue";
import type { Component } from "vue";
import "./index.css";
import i18n from "@/config/i18n";
export function renderModal(content: Component, props: Record<any, any>) {
// 遮罩层
const mask = document.createElement("div");
mask.classList.add("modal-mask-container");
const app = createApp({
name: "ModalApp",
render() {
return container;
},
});
// 实际组件(模态框)
const container = h(
// 定义
{
name: "ModalContainer",
setup(_, context) {
return () =>
h(
"div",
{
class: "modal-container",
},
[renderSlot(context.slots, "default")]
);
},
},
null,
// 使用
{
default: () =>
h(content, {
...props,
onClose() {
console.log("关闭事件触发了!");
},
}),
}
);
// 按照插件
app.use(i18n);
// 将虚拟DOM渲染再真实DOM中
app.mount(mask);
document.body.appendChild(mask);
}
2.直接导入 i18n 插件来使用
对应组件中直接导入i18n
插件在模板中使用,就不再需要 app 实例了,因为我们的组件模板没有使用$t
来渲染文本就不会产生错误了。
下面是一个模态框的封装: 这样就非常好了。
js
import { h, render, renderSlot } from "vue";
import type { Component } from "vue";
import "./index.css";
/**
* 渲染模态框
* @param content 模态框内容组件
* @param props 模态框内容组件的props
*/
export function renderModal(content: Component, props: Record<any, any>) {
// 容器
const mask = document.createElement("div");
mask.classList.add("modal-mask-container");
// 虚拟DOM
const container = h(
// 定义组件
{
name: "ModalContainer",
setup(_, context) {
return () =>
h(
"div",
{
class: "modal-container",
},
[renderSlot(context.slots, "default")]
);
},
},
null,
// 使用组件
{
default: () =>
h(content, {
...props,
onClose() {
console.log("关闭事件触发了!");
},
}),
}
);
// 渲染
render(container, mask);
// 挂载
document.body.appendChild(mask);
}
import { h, render, renderSlot } from "vue";
import type { Component } from "vue";
import "./index.css";
/**
* 渲染模态框
* @param content 模态框内容组件
* @param props 模态框内容组件的props
*/
export function renderModal(content: Component, props: Record<any, any>) {
// 容器
const mask = document.createElement("div");
mask.classList.add("modal-mask-container");
// 虚拟DOM
const container = h(
// 定义组件
{
name: "ModalContainer",
setup(_, context) {
return () =>
h(
"div",
{
class: "modal-container",
},
[renderSlot(context.slots, "default")]
);
},
},
null,
// 使用组件
{
default: () =>
h(content, {
...props,
onClose() {
console.log("关闭事件触发了!");
},
}),
}
);
// 渲染
render(container, mask);
// 挂载
document.body.appendChild(mask);
}