关于 data-provider
data-provider 是面向 Vue 开发者的数据提供者模式指南,介绍如何将数据获取与状态管理从界面展示中分离。它借助无渲染组件和作用域插槽,让多个组件复用数据逻辑并采用不同呈现方式。
主要功能
- 01封装数据获取与状态管理
- 02通过作用域插槽传递数据
- 03向子组件提供加载状态和操作
- 04将展示逻辑与数据逻辑分离
适用场景
- 01多个组件以不同方式展示同一数据
- 02集中管理数据获取逻辑
- 03替换界面组件而保留数据逻辑
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ patternsdev/ skills --skill data-provider安装后在 Claude Code 中输入/data-provider确认已加载。
详细介绍
数据提供者模式
目录
在之前的一篇文章中,我们了解到无渲染组件如何帮助将组件逻辑与其呈现方式分离。当我们需要创建可应用于不同 UI 实现的可复用逻辑时,这种做法就很有用。
无渲染组件还能让我们运用另一种有用的模式,即数据提供者模式。
何时使用
- 多个组件需要使用相同的数据,但以不同方式展示时
- 需要集中处理数据获取逻辑,同时避免将其与特定 UI 组件耦合时
何时不使用
- 当组合式函数无需额外的组件层就能处理数据逻辑时(Vue 3+)
- 只有一个组件使用数据时——使用组合式函数或内联获取会更简单
- 数据提供者的嵌套增加了间接层,使模板更难理解时
说明
- 创建一个数据提供者组件,其模板只包含一个带有作用域插槽属性的
<slot> - 将数据、加载状态和操作方法作为作用域插槽属性传递给子组件
- 在父组件中使用
v-slot解构来访问提供的数据 - 让子组件专注于呈现;所有数据逻辑都由数据提供者处理
详情
数据提供者模式
数据提供者模式是一种设计模式,它通过专注于为组件提供数据和状态管理能力来补充 Vue 中的无渲染组件模式,而不关注数据如何渲染或显示。
在数据提供者模式中,数据提供者组件封装获取、管理和向子组件公开数据的逻辑。子组件随后可以使用这些数据进行自身的渲染或行为处理。
这种模式促进了关注点分离:数据提供者组件负责处理与数据相关的任务,而子组件则可以专注于展示和交互。
让我们通过一个示例来说明数据提供者模式。假设有一个简单的应用,用来显示一则有趣笑话的铺垫,然后是它的包袱。为了让示例自成一体,我们将使用本地内存数据源,而不依赖外部 API。
const jokes = [
{ id: 1, setup: "Why did the dev go broke?", punchline: "Because they used up all their cache." },
{ id: 2, setup: "Why do functions love TypeScript?", punchline: "Because it keeps their arguments in order." },
];
首先,我们将创建一个名为 DataProvider 的数据提供者组件,由它负责加载笑话。在组件的 <script> 部分,我们将从 Vue 导入 ref() 和 reactive() 函数,定义一个本地数据源,并设置 data 和 loading 响应式属性,用于记录选中的笑话和加载状态。
<script setup>
import { ref, reactive } from "vue";
const jokes = [
{ id: 1, setup: "Why did the dev go broke?", punchline: "Because they used up all their cache." },
{ id: 2, setup: "Why do functions love TypeScript?", punchline: "Because it keeps their arguments in order." },
];
const data = reactive({
setup: null,
punchline: null,
});
const loading = ref(false);
</script>
接下来,我们可以在 DataProvider 组件中创建一个 fetchJoke() 函数,以模拟异步加载数据。
const fetchJoke = async () => {
loading.value = true;
try {
await new Promise((resolve) => setTimeout(resolve, 300));
const jokeData = jokes[Math.floor(Math.random() * jokes.length)];
data.setup = jokeData.setup;
data.punchline = jokeData.punchline;
} catch (error) {
console.error("Error loading joke:", error);
} finally {
loading.value = false;
}
};
准备好获取函数后,我们可以使用 onMounted() 生命周期钩子,在组件挂载时调用它。
import { ref, reactive, onMounted } from "vue";
// ...
onMounted(() => {
fetchJoke();
});
数据提供者组件的关键要素是,其模板只包含一个 <slot> 元素。这个插槽会使用作用域插槽向子组件提供获取到的数据和相关方法。
<template>
<slot :data="data" :loading="loading" :fetchJoke="fetchJoke"></slot>
</template>
DataProvider 组件将 data、loading 和 fetchJoke 作为作用域插槽属性传递。这意味着,放在 DataProvider 内的任何子组件都可以访问这些属性。
现在,让我们创建一个用于展示笑话数据的 JokeCard 组件。
<template>
<div class="joke-card">
<p v-if="loading">Loading...</p>
<div v-else>
<p class="setup">{{ data.setup }}</p>
<p class="punchline">{{ data.punchline }}</p>
</div>
<button @click="fetchJoke">Get Another Joke</button>
</div>
</template>
<script setup>
defineProps(["data", "loading", "fetchJoke"]);
</script>
JokeCard 组件是一个简单的展示组件。它接收 data、loading 和 fetchJoke 作为属性,并渲染笑话数据以及一个用于获取新笑话的按钮。
现在,我们在 App 组件中使用 DataProvider 组件,将所有内容组合起来。我们把 JokeCard 组件放在 DataProvider 内,并将作用域插槽属性传递给它:
<template>
<DataProvider v-slot="{ data, loading, fetchJoke }">
<JokeCard :data="data" :loading="loading" :fetchJoke="fetchJoke" />
</DataProvider>
</template>
<script setup>
import DataProvider from "./components/DataProvider.vue";
import JokeCard from "./components/JokeCard.vue";
</script>
通过这种设置,DataProvider 负责所有数据获取和管理,而 JokeCard 则只专注于显示数据。这种清晰的分离让我们可以轻松换用其他展示组件,而无需改动数据获取逻辑。
以下情况尤其适合使用数据提供者模式:
- 多个组件需要使用相同的数据,但以不同方式展示。
- 希望集中管理数据获取逻辑,同时避免与特定 UI 组件紧密耦合。
- 希望让组件专注于单一职责。