Vue组件内的模板复用
刀刀
8/8/2025
0 字
0 分钟
- 来源:远方os;视频地址:前往学习
场景复现
下面有一个场景:一个模块会有多个地方复用,代码如下:
vue
<template>
<div class="container">
<div class="title">标题</div>
<div class="content">内容</div>
</div>
<div>111</div>
<div class="container">
<div class="title">标题</div>
<div class="content">内容</div>
</div>
<p>2222</p>
<div>333</div>
<div class="container">
<div class="title">标题</div>
<div class="content">内容</div>
</div>
</template>
如上方代码所示,.container
内容是重复的,如何在不把他们独立封装为一个子组件的情况下实现模块内复用呢?
解决方案
组件即函数
在 Vue 中,组件即函数,我们写的 <template></template>
后续会被转化为一个函数,所以我们可以通过函数的参数来实现复用。
创建一个对象,里面有一个 setup
函数,即生命周期的 setup
,接收两个参数,第二个参数可以解构出 slots
插槽,然后通过 slots.default()
获取插槽内容。故可如下实现:
js
let render;
const DefineTemplate = {
setup(_, { slots }) {
return () => {
render = slots.default();
return render();
}
}
}
const UseTemplate = () => render()
vue
<template>
<DefineTemplate>
<div class="container">
<div class="title">标题</div>
<div class="content">内容</div>
</div>
</DefineTemplate>
<div>111</div>
<UseTemplate></UseTemplate>
<p>2222</p>
<div>333</div>
<UseTemplate></UseTemplate>
</template>
从 DefineTemplate
对象的 setup
生命周期中,把相同的模块保存到 render
中,然后通过调用 render()
函数来复用,这样就能实现模块内复用相同的组件。
抽离封装复用
这样只是在一个 .vue
文件内可以使用,不妨抽离出来,封装成一个公共的函数,这样每个需要的 .vue
文件都能使用。
js
export const createReusableTemplate() {
let render;
const DefineTemplate = {
setup(_, { slots }) {
return () => {
render = slots.default();
return render();
}
}
}
const UseTemplate = () => render()
return [DefineTemplate, UseTemplate]
}
vue
<script setup>
import { createReusableTemplate } from './utils.js'
const [DefineTemplate, UseTemplate] = createReusableTemplate()
</script>
<template>
<DefineTemplate>
<div class="container">
<div class="title">标题</div>
<div class="content">内容</div>
</div>
</DefineTemplate>
<UseTemplate></UseTemplate>
<div>111</div>
<UseTemplate></UseTemplate>
<p>2222</p>
<div>333</div>
<UseTemplate></UseTemplate>
</template>
抽离出来放到一个函数中,最后 return
一个数组,外部使用时通过数组解构还可以自定义命名,这样就能实现模块内复用相同的组件。
⚠️ 注意
DefineTemplate
对象的 setup
方法这里去掉了副作用,不再 return
返回默认插槽的内容,而是单纯的保存到 render
变量中。
支持传参数
基础需求实现了,接下来要开始细化,比如如果在使用时想要传参数,这该如何解决呢?例如:
vue
<script setup>
import { createReusableTemplate } from './utils.js'
const [DefineTemplate, UseTemplate] = createReusableTemplate()
</script>
<template>
<DefineTemplate>
<div class="container">
<div class="title">标题</div>
<div class="content">内容</div>
</div>
</DefineTemplate>
<UseTemplate title="header" @foo="console.log"></UseTemplate> // [!code focus]
<div>111</div>
<UseTemplate title="main"></UseTemplate> // [!code focus]
<p>2222</p>
<div>333</div>
<UseTemplate title="footer"></UseTemplate> // [!code focus]
</template>
组件 UseTemplate
实际上就是 UseTemplate
函数,传的变量和事件都可以在 UseTemplate
函数的第一个参数接收。
js
export const createReusableTemplate() {
let render;
const DefineTemplate = {
setup(_, { slots }) {
return () => {
render = slots.default();
}
}
}
const UseTemplate = (props) => {
console.log(props) // { title: 'header', foo: ƒ }
return render()
}
return [DefineTemplate, UseTemplate]
}
得知这一点后,就知道该怎么实现传参了,步骤如下:
- 函数
UseTemplate
接收到参数后,传给render
方法 render
接收到参数,相当于默认插槽slots.default
接收到参数- 组件
DefineTemplate
通过v-slot="data"
的方式从默认插槽中获取到数据 - 使用
代码最终可以修改为如下形式:
js
export const createReusableTemplate() {
let render;
const DefineTemplate = {
setup(_, { slots }) {
return () => {
render = slots.default();
}
}
}
const UseTemplate = (props) => render(props)
return [DefineTemplate, UseTemplate]
}
vue
<script setup>
import { createReusableTemplate } from './utils.js'
const [DefineTemplate, UseTemplate] = createReusableTemplate()
</script>
<template>
<DefineTemplate>
<div class="container" v-slot="{ title, onFoo }"> // [!code focus]
<div class="title">{{ title }}</div> // [!code focus]
<div class="content" @click="onFoo(111)">内容</div> // [!code focus]
</div>
</DefineTemplate>
<UseTemplate title="header" @foo="console.log"></UseTemplate> // [!code focus]
<div>111</div>
<UseTemplate title="main"></UseTemplate> // [!code focus]
<p>2222</p>
<div>333</div>
<UseTemplate title="footer"></UseTemplate> // [!code focus]
</template>
⚠️ 注意
方法内部会被转化为 on
+ 首字母大写的形式。