关于 renderless-components
面向希望复用组件逻辑、同时让父组件决定呈现方式的 Vue 开发者。介绍通过作用域插槽提供逻辑的无渲染组件,并说明何时组合式函数更简单。
主要功能
- 01分离组件逻辑与呈现
- 02通过作用域插槽提供数据和方法
- 03由父组件决定渲染界面
- 04比较无渲染组件与组合式函数
适用场景
- 01在不同界面设计中复用切换逻辑
- 02为组件库构建基于组件的 API
- 03在模板层面组合界面
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ patternsdev/ skills --skill renderless-components安装后在 Claude Code 中输入/renderless-components确认已加载。
详细介绍
无渲染组件
目录
无渲染组件是 Vue 中的一种模式,它将组件的逻辑与呈现分离。这种模式提供了一种封装功能的方式,且_不规定组件的视觉呈现形式_。换句话说,无渲染组件只关注逻辑和行为,而将渲染交给父组件。
当我们需要创建可复用的逻辑,并将其应用于不同的 UI 实现时,无渲染组件特别有用。通过将逻辑抽象到无渲染组件中,我们可以轻松地在各种场景中复用,而无需重复编写代码。
适用场景
- 当你需要在视觉呈现完全不同的组件之间复用逻辑时,使用这种模式
- 当你需要在组件库中提供基于组件的 API 时,这种模式会很有帮助
不适用场景
- 当组合式函数可以实现相同的逻辑复用,且无需额外的组件嵌套时(Vue 3+)
- 当无渲染组件封装的逻辑很简单,用普通函数或组合式函数处理会更清晰时
- 当作用域插槽 API 比直接返回组合式函数值更难理解时
说明
- 创建一个组件,通过带有作用域插槽属性的单个
<slot>提供数据和方法 - 在父组件中使用
v-slot解构,以访问所提供的数据和方法 - 在 Vue 3 中,优先使用组合式函数而非无渲染组件,以避免额外的组件嵌套
- 当你希望在模板层面进行组合,或需要基于组件的 API 时,使用无渲染组件
详情
切换,切换,再切换
想象一下,你有一个切换 UI 元素,需要在应用的不同位置使用,但每个实例的视觉呈现形式可能不同。有些切换项可能显示为按钮,另一些则可能显示为复选框或开关。
我们可以直接创建三个不同的切换组件,但可以发现,每个切换元素的逻辑和行为都相同。每个切换项都有非活动和活动两种状态,并通过组件的数据属性(例如 checked)进行跟踪。点击切换项时,其组件状态会在非活动和活动之间切换(即 checked = !checked)。
我们很快就能发现,可以通过提取通用逻辑和行为,创建一种更可复用的模式,这样就不必在每个切换组件中反复定义状态和切换方法。这里很适合使用组合式函数,因为组合式函数能让我们封装并共享不同切换组件之间通用的有状态逻辑。
useCheckboxToggle:
import { ref } from "vue";
export function useCheckboxToggle() {
const checkbox = ref(false);
const toggleCheckbox = () => {
checkbox.value = !checkbox.value;
};
return {
checkbox,
toggleCheckbox,
};
}
有了这个组合式函数,我们就可以在各种切换组件中使用 useCheckboxToggle() 函数,共享通用的状态和切换逻辑。
不过,我们还可以采用另一种方式,利用 Vue 的插槽机制——无渲染组件模式。
无渲染组件
Vue 中的无渲染组件是指一种封装逻辑,并通过作用域插槽向子组件提供数据,但自身不渲染任何标记的组件。数据如何呈现由父组件决定。
下面是一个简单的无渲染 Toggle 组件:
<script setup>
import { ref } from "vue";
const checked = ref(false);
const toggle = () => {
checked.value = !checked.value;
};
</script>
<template>
<slot :checked="checked" :toggle="toggle"></slot>
</template>
Toggle 组件自身不渲染任何 HTML。它只通过作用域插槽提供数据(checked 和 toggle)。父组件现在可以使用这些数据,并渲染所需的任意 UI。
将无渲染 Toggle 用作按钮:
<template>
<Toggle v-slot="{ checked, toggle }">
<button @click="toggle">
{{ checked ? "ON" : "OFF" }}
</button>
</Toggle>
</template>
将无渲染 Toggle 用作复选框:
<template>
<Toggle v-slot="{ checked, toggle }">
<label>
<input type="checkbox" :checked="checked" @change="toggle" />
{{ checked ? "Checked" : "Unchecked" }}
</label>
</Toggle>
</template>
将无渲染 Toggle 用作开关:
<template>
<Toggle v-slot="{ checked, toggle }">
<div
class="switch"
:class="{ active: checked }"
@click="toggle"
>
<div class="switch-handle"></div>
</div>
</Toggle>
</template>
在这三种情况下,同一个无渲染 Toggle 组件都提供切换逻辑,但渲染形式完全不同!
组合式函数与无渲染组件
组合式函数和无渲染组件都能实现跨组件复用逻辑。不过,两者之间有一些区别:
组合式函数:
- 逻辑封装在常规 JavaScript 函数中。
- 可直接在
<script setup>或setup()中使用。 - 不会引入额外的组件层级。
无渲染组件:
- 逻辑封装在 Vue 组件中。
- 使用作用域插槽向子组件传递数据。
- 会在模板中增加额外的组件层级。
通常,在 Vue 3 中更推荐使用组合式函数,因为它不会增加额外的组件嵌套。不过,如果你想提供基于组件的 API(例如在组件库中),或者需要在模板层面进行组合,无渲染组件会很有用。