Published on

译: 数据提供者模式

文章翻译至: Data Provider Pattern

目录

数据提供者模式是一种设计模式,它补充了 Vue 中的无渲染组件模式,它专注于为组件提供数据和状态管理功能,而不关心数据如何渲染或显示。

在数据提供者模式中,数据提供者组件封装了用于获取、管理数据并将数据公开给其子组件的逻辑。之后子组件可以使用这些数据并在自己的渲染或行为中使用它。

让我们用一个例子来说明数据提供者模式。

考虑一个简单的应用程序,它显示一个有趣的笑话设置及其妙语。

为了帮助我们随机显示不同的笑话,我们将使用免费的公共 API 端点 https://official-joke-api.appspot.com/random_joke,它会随机返回 JSON 格式的笑话。

# https://official-joke-api.appspot.com/random_joke

{
  "type": "general",
  "setup": "How good are you at Power Point?",
  "punchline": "I Excel at it.",
  "id": 129
}

我们首先创建一个名为 DataProvider 的数据提供程序组件,它将负责从 API 中获取笑话。在组件的 <script> 部分中,我们将从 Vue 库导入 ref()reactive() 函数,将 URL 值分配给常量,然后设置 dataloading 响应式属性来捕获 API 请求的数据和加载状态。

<script setup>
  import { ref, reactive } from "vue";

  const API_ENDPOINT_URL = "https://official-joke-api.appspot.com/random_joke";

  const data = reactive({
    setup: null,
    punchline: null,
  });
  const loading = ref(false);
</script>

然后,我们将创建一个名为 fetchJoke() 的异步函数,负责从指定的 API 端点获取笑话。该函数将:

  • 首先将 loading 反应值设置为 true ,表示正在获取数据。

  • 使用fetch() 发送 GET 请求到API。

  • 使用 response.json() 方法将 API 的响应转换为 JSON 格式。

  • 从获取的请求数据中提取 setuppunchline 值,并将它们赋值给 data 对象中的相应属性。

  • 最后,将 loading 值设置回 false ,表示数据已被获取。

经过这些更改,我们的 fetchJoke() 函数将如下所示:

<script setup>
  import { ref, reactive } from "vue";

  const API_ENDPOINT_URL = "https://official-joke-api.appspot.com/random_joke";

  const data = reactive({
    setup: null,
    punchline: null,
  });
  const loading = ref(false);

  const fetchJoke = async () => {
    loading.value = true;

    const response = await fetch(API_ENDPOINT_URL);
    const responseData = await response.json();

    data.setup = responseData.setup;
    data.punchline = responseData.punchline;
    loading.value = false;
  };

  fetchJoke();
</script>

注意到我们在 <script> 部分末尾执行了 fetchJoke() 函数吗?这确保了在渲染 DataProvider 组件时立即获取数据。

我们要做的最后一件事是使 dataloading 属性在 DataProvider 组件的使用者中可用。为此,我们可以将这些属性传递给将放置在 <template> 部分中的 <slot> 元素。

<template>
  <slot :checkbox="checkbox" :toggleCheckbox="toggleCheckbox"></slot>
</template>

<script setup>
  import { ref, reactive } from "vue";

  const API_ENDPOINT_URL = "https://official-joke-api.appspot.com/random_joke";

  const data = reactive({
    setup: null,
    punchline: null,
  });
  const loading = ref(false);

  const fetchJoke = async () => {
    loading.value = true;

    const response = await fetch(API_ENDPOINT_URL);
    const responseData = await response.json();

    data.setup = responseData.setup;
    data.punchline = responseData.punchline;
    loading.value = false;
  };

  fetchJoke();
</script>

随着我们的无渲染数据提供程序组件的完成,我们现在可以在我们的应用程序中使用它。在父应用程序组件中,我们将导入 DataProvider 组件并将其放置在模板中。

<template>
  <DataProvider v-slot="{ data, loading }">
    <!-- ... -->
  </DataProvider>
</template>

<script setup>
  import DataProvider from "./components/DataProvider.vue";
</script>

通过渲染 <DataProvider> 组件,我们可以发出请求到API以获取数据,并通过 v-slot 指令来访问请求的数据和加载值。

<DataProvider> 组件声明中,我们可以创建 UI,如果请求处于加载状态,该 UI 将显示加载消息,或者在数据可用时显示数据。

<template>
  <DataProvider v-slot="{ data, loading }">
    <div class="joke-section">
      <p v-if="loading">Joke is loading...</p>
      <p v-if="!loading">{{ data.setup }}</p>
      <p v-if="!loading">{{ data.punchline }}</p>
    </div>
  </DataProvider>
</template>

<script setup>
  import DataProvider from "./components/DataProvider.vue";
</script>

保存更改时,我们将看到一条简短的加载消息,然后是一个随机笑话。

如果我们需要渲染此数据的另一个实例,甚至使用不同的模板,我们可以简单地复用 <DataProvider> 组件并创建我们想要显示的新子元素。

<template>
  <DataProvider v-slot="{ data, loading }">
    <div class="joke-section">
      <p v-if="loading">Joke is loading...</p>
      <p v-if="!loading">{{ data.setup }}</p>
      <p v-if="!loading">{{ data.punchline }}</p>
    </div>
  </DataProvider>

  <DataProvider v-slot="{ data, loading }">
    <p v-if="loading">Hold on one sec...</p>
    <div v-else class="joke-section">
      <details>
        <summary>{{ data.setup }}</summary>
        <p>{{ data.punchline }}</p>
      </details>
    </div>
  </DataProvider>
</template>

<script setup>
  import DataProvider from "./components/DataProvider.vue";
</script>

通过数据提供者模式,我们能够以解耦和可重用的方式管理数据并将其提供给不同的元素/组件。通过将 API 获取逻辑抽象为无渲染组件,我们可以在各种上下文中重用 API 数据的请求,而无需重复代码。

使用Composition API

import { ref, reactive } from "vue";

const API_ENDPOINT_URL = "https://official-joke-api.appspot.com/random_joke";

export function useGetJoke() {
  const data = reactive({
    setup: null,
    punchline: null,
  });
  const loading = ref(false);

  const fetchJoke = async () => {
    loading.value = true;

    const response = await fetch(API_ENDPOINT_URL);
    const responseData = await response.json();

    data.setup = responseData.setup;
    data.punchline = responseData.punchline;
    loading.value = false;
  };

  fetchJoke();

  return { data, loading };
}

在我们的组件实例中,我们可以导入并使用可组合函数来获取特定请求的 dataloading 状态。

<template>
  <div class="joke-section">
    <p v-if="loading">Joke is loading...</p>
    <p v-if="!loading">{{ data.setup }}</p>
    <p v-if="!loading">{{ data.punchline }}</p>
  </div>
</template>

<script setup>
  import { useGetJoke } from "./composables/useGetJoke";

  const { data, loading } = useGetJoke();
</script>

表现与之前无渲染组件一样。

数据提供者模式通过让父组件根据公开的数据和无渲染组件的行为来渲染适当的 UI,从而帮助将组件的逻辑与其表示分离。然而,由于能够在 Vue 3 中创建可重用的可组合函数,因此可组合项也可以用于大多数可以使用数据提供程序模式的情况。

在考虑使用数据提供程序模式还是使用可组合函数时,我们建议尽可能使用可组合函数,因为它避免了每次需要完成数据获取时都需要渲染组件实例(这可能会导致性能开销)。

此外,如果您使用 Pinia 等状态管理工具来管理向组件提供数据的方式,那么您很可能会在store的 actions() 中发出 API 请求。有了这种状态管理模式,使用数据提供者组件模式的需求就变得不那么重要了。

End.