renderless-components
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRenderless Components
Renderless Components
Table of Contents
目录
Renderless components are a pattern in Vue that separates the logic of a component from its presentation. The pattern provides a way to encapsulate functionality without dictating the visual representation of the component. In other words, a renderless component focuses solely on the logic and behavior, while leaving the rendering to the parent component.
Renderless components are particularly useful when we need to create reusable logic that can be applied to different UI implementations. By abstracting the logic into a renderless component, we can easily reuse it in various contexts without duplicating code.
When to Use
适用场景
- Use this when you need to reuse logic across components with completely different visual representations
- This is helpful for providing a component-based API in a component library
- 当你需要在具有完全不同视觉表现的组件之间复用逻辑时使用
- 这有助于在组件库中提供基于组件的API
When NOT to Use
不适用场景
- When composables achieve the same logic reuse without extra component nesting (Vue 3+)
- When the renderless component wraps trivial logic that a simple function or composable handles more clearly
- When the scoped slot API becomes harder to understand than a direct composable return value
- 当使用Composables可以实现相同的逻辑复用,且无需额外组件嵌套时(Vue 3+)
- 当Renderless Component封装的逻辑十分简单,使用普通函数或Composables能更清晰地处理时
- 当scoped slots API比直接使用Composables返回值更难理解时
Instructions
使用说明
- Create a component that provides data and methods through a single with scoped slot props
<slot> - Use destructuring in parent components to access the provided data and methods
v-slot - Prefer composables over renderless components in Vue 3 to avoid extra component nesting
- Use renderless components when you want template-level composition or a component-based API
- 创建一个组件,通过单个结合作用域插槽属性来提供数据和方法
<slot> - 在父组件中使用解构来访问提供的数据和方法
v-slot - 在Vue 3中优先使用Composables而非Renderless Components,以避免额外的组件嵌套
- 当你需要模板层面的组合或基于组件的API时,使用Renderless Components
Details
详细说明
Toggle, toggle, toggle
切换功能示例
Imagine you have a toggle UI element that needs to be used in different parts of your application, but each instance may have a different visual representation. Some toggles might be displayed as buttons, while others might be checkboxes or switches.
We could just create three different toggle components, however, we can observe that each toggle element has the same logic and behavior. Each toggle has an inactive and active state that's being tracked with a component data property (e.g. ). When a toggle is clicked, its component state is switched from inactive to active and vice versa (i.e. ).
checkedchecked = !checkedRight away, we can see that we can create a more reusable pattern by extracting the common logic and behavior in such a way that we don't have to repeatedly define the state and toggle methods in each individual toggle component. This is a great case to use composables since composables will allow us to encapsulate and share the common stateful logic across the different toggle components.
useCheckboxToggle:
js
import { ref } from "vue";
export function useCheckboxToggle() {
const checkbox = ref(false);
const toggleCheckbox = () => {
checkbox.value = !checkbox.value;
};
return {
checkbox,
toggleCheckbox,
};
}With this composable, we can now use the function in our various toggle components to share the common state and toggle logic.
useCheckboxToggle()However, there's another approach we can take that leverages Vue's slot mechanism — the renderless component pattern.
假设你有一个切换UI元素,需要在应用的不同部分使用,但每个实例可能有不同的视觉表现。有些切换可能显示为按钮,有些可能是复选框或开关。
我们可以创建三个不同的切换组件,但可以发现每个切换元素都具有相同的逻辑和行为。每个切换都有一个非激活和激活状态,通过组件数据属性(如)进行跟踪。当点击切换时,组件状态会在非激活和激活之间切换(即)。
checkedchecked = !checked我们可以立即发现,通过提取通用逻辑和行为,我们可以创建一种更具复用性的模式,无需在每个单独的切换组件中重复定义状态和切换方法。这是使用Composables的绝佳场景,因为Composables允许我们封装并在不同的切换组件之间共享通用的有状态逻辑。
useCheckboxToggle:
js
import { ref } from "vue";
export function useCheckboxToggle() {
const checkbox = ref(false);
const toggleCheckbox = () => {
checkbox.value = !checkbox.value;
};
return {
checkbox,
toggleCheckbox,
};
}有了这个Composable,我们现在可以在各种切换组件中使用函数来共享通用状态和切换逻辑。
useCheckboxToggle()不过,我们还有另一种方法可以利用Vue的插槽机制——Renderless Component模式。
The Renderless Component
Renderless Component
A renderless component in Vue is a component that encapsulates logic and provides data to its children via scoped slots, without rendering any markup of its own. The parent component decides how the data is presented.
Here's a simple renderless component:
Togglehtml
<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>The component doesn't render any HTML of its own. It only provides data ( and ) through a scoped slot. The parent component can now consume this data and render whatever UI it wants.
TogglecheckedtoggleUsing the renderless Toggle as a button:
html
<template>
<Toggle v-slot="{ checked, toggle }">
<button @click="toggle">
{{ checked ? "ON" : "OFF" }}
</button>
</Toggle>
</template>Using the renderless Toggle as a checkbox:
html
<template>
<Toggle v-slot="{ checked, toggle }">
<label>
<input type="checkbox" :checked="checked" @change="toggle" />
{{ checked ? "Checked" : "Unchecked" }}
</label>
</Toggle>
</template>Using the renderless Toggle as a switch:
html
<template>
<Toggle v-slot="{ checked, toggle }">
<div
class="switch"
:class="{ active: checked }"
@click="toggle"
>
<div class="switch-handle"></div>
</div>
</Toggle>
</template>In all three cases, the same renderless component provides the toggle logic, but the rendering is entirely different!
ToggleVue中的Renderless Component是一种封装逻辑并通过scoped slots向其子组件提供数据的组件,它自身不会渲染任何标记。父组件决定如何展示这些数据。
以下是一个简单的无渲染组件:
Togglehtml
<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>这个组件自身不会渲染任何HTML。它仅通过作用域插槽提供数据(和)。父组件现在可以使用这些数据并渲染任何想要的UI。
Togglecheckedtoggle将无渲染Toggle用作按钮:
html
<template>
<Toggle v-slot="{ checked, toggle }">
<button @click="toggle">
{{ checked ? "ON" : "OFF" }}
</button>
</Toggle>
</template>将无渲染Toggle用作复选框:
html
<template>
<Toggle v-slot="{ checked, toggle }">
<label>
<input type="checkbox" :checked="checked" @change="toggle" />
{{ checked ? "Checked" : "Unchecked" }}
</label>
</Toggle>
</template>将无渲染Toggle用作开关:
html
<template>
<Toggle v-slot="{ checked, toggle }">
<div
class="switch"
:class="{ active: checked }"
@click="toggle"
>
<div class="switch-handle"></div>
</div>
</Toggle>
</template>在这三种情况下,同一个无渲染组件提供了切换逻辑,但渲染的UI完全不同!
ToggleComposables vs. Renderless Components
Composables vs. Renderless Components
Both composables and renderless components achieve the goal of reusing logic across components. However, there are some differences:
Composables:
- Logic is encapsulated in a regular JavaScript function.
- Can be used directly in or
<script setup>.setup() - Don't involve any additional component layers.
Renderless components:
- Logic is encapsulated in a Vue component.
- Use scoped slots to pass data to children.
- Add an extra component layer in the template.
In general, composables are the preferred approach in Vue 3 since they don't add extra component nesting. However, renderless components can be useful when you want to provide a component-based API (e.g., in a component library) or when you need template-level composition.
Composables和Renderless Components都能实现组件间的逻辑复用,但两者存在一些差异:
Composables:
- 逻辑封装在普通JavaScript函数中
- 可直接在或
<script setup>中使用setup() - 不涉及任何额外的组件层级
Renderless Components:
- 逻辑封装在Vue组件中
- 使用scoped slots向子组件传递数据
- 在模板中添加了额外的组件层级
一般来说,在Vue 3中优先使用Composables,因为它们不会增加额外的组件嵌套。不过,当你希望提供基于组件的API(例如在组件库中)或需要模板层面的组合时,Renderless Components会很有用。