设计模式
提供/注入
在管理父组件和子组件之间的数据时,Vue 允许我们使用称为 **prop** 的东西将数据从父组件传递给子组件。Props 只能单向流动,从父组件到子组件(以及更下层)。当父元素上的状态发生变化时,Vue 将重新渲染依赖于这些值的组件。
在大多数情况下,使用 props 运作良好。然而,当在包含大量组件的组件树的复杂应用程序中工作时,props 可能会变得难以维护,因为 props 需要在组件树中每个组件中声明。
在考虑如何管理大量组件之间的数据时,通常最好寻求一种解决方案,该解决方案能够以可维护和可管理的方式管理应用程序级状态(例如,创建可重用的存储,使用 Pinia 等)。我们在 状态管理 指南中对此进行了更详细的说明。
但是,Vue 还提供了一种特定的模式来帮助避免在 Vue 应用程序中对复杂的 prop 传递的需求,这种模式被称为提供/注入模式。
提供/注入
Vue 中的 provide()
函数允许我们在不进行prop 传递(即,在每个级别手动传递 props)的情况下,将数据传递到组件树。另一方面,inject()
选项用于在子组件中从其父级或任何祖先组件访问提供的数据或方法。
我们将通过一个简单的示例来说明如何做到这一点。假设我们有一个名为 App
的父组件,它希望与子组件 ChildComponent
共享一段数据。与其将此数据作为 prop 传递,不如在父组件中使用 provide()
将数据提供给其所有子组件。
<template>
<div id="app">
<ChildComponent />
</div>
</template>
<script setup>
import { provide } from "vue";
import ChildComponent from "./components/ChildComponent";
provide("data", "Data from parent!");
</script>
然后,我们可以在 ChildComponent
中使用 inject()
函数访问此提供的数据。
<template>
<div>
<p>{{ data }}</p>
</div>
</template>
<script setup>
import { inject } from "vue";
const data = inject("data");
</script>
通过在子组件 (ChildComponent
) 中指定 inject("data")
,我们可以直接从父组件访问提供的 data
值。然后我们将 data
绑定到模板以显示其值。
1<template>2 <div id="app">3 <ChildComponent />4 </div>5</template>67<script setup>8import { provide } from "vue";9import ChildComponent from "./components/ChildComponent";1011provide("data", "Data from parent!");12</script>
使用 provide/inject,我们将会注意到与上面相同的行为,即使我们在组件层次结构树中有多个子组件。例如,假设我们有 <ChildComponent />
、<ChildComponent2 />
、<ChildComponent3 />
、<ChildComponent4 />
和 <ChildComponent5 />
组件,其中每个子组件都是另一个子组件的父组件。
<!-- ChildComponent5 -->
<template>
<div>
<p>{{ data }}</p>
</div>
</template>
<script setup>
import { inject } from "vue";
const data = inject("data");
</script>
<!-- ------------- -->
<!-- ChildComponent4 -->
<template>
<ChildComponent5 />
</template>
<script setup>
import ChildComponent5 from "./ChildComponent5";
</script>
<!-- ------------- -->
<!-- ChildComponent3 -->
<template>
<ChildComponent4 />
</template>
<script setup>
import ChildComponent4 from "./ChildComponent4";
</script>
<!-- ------------- -->
<!-- ChildComponent2 -->
<template>
<ChildComponent3 />
</template>
<script setup>
import ChildComponent3 from "./ChildComponent3";
</script>
<!-- ------------- -->
<!-- ChildComponent -->
<template>
<ChildComponent2 />
</template>
<script setup>
import ChildComponent2 from "./ChildComponent2";
</script>
<!-- ------------- -->
<!-- App -->
<template>
<div id="app">
<ChildComponent />
</div>
</template>
<script setup>
import { provide } from "vue";
import ChildComponent from "./components/ChildComponent";
provide("data", "Data from parent!");
</script>
<!-- ------------- -->
来自父组件 <App />
的数据将在 <ChildComponent5 />
组件中渲染,而无需通过 provide/inject 将数据传递到树中的每个组件!
1<template>2 <div id="app">3 <ChildComponent />4 </div>5</template>67<script setup>8import { provide } from "vue";9import ChildComponent from "./components/ChildComponent";1011provide("data", "Data from parent!");12</script>
除了能够从父组件provide()
数据外,我们还可以将 provide()
提升到应用程序级别(即,我们在其中实例化 Vue 应用程序的地方)。
import { createApp } from "vue";
import App from "./App.vue";
import "./styles.css";
const app = createApp(App);
// app-level provide
app.provide("data", "Data from parent!");
app.mount("#app");
由于应用程序级提供将数据提供给所有组件,因此它们在创建 插件 时非常有用 - 自包含的代码,可为整个 Vue 应用程序添加功能。
Props 与 provide/inject
我们何时在 props 和 provide/inject 模式之间进行选择?两种方法都有其优点和缺点。
使用 props
- 我们遵循一种清晰的模式,将数据逐级传递(优点)。
- 但是,如果我们的组件层次结构树包含大量组件,则逐级传递 props 数据的过程会变得很繁琐(缺点)。
使用 provide/inject
- 子组件可以直接从位于多个级别之上的父组件访问数据,从而消除了在每个级别传递数据的需求(优点)。
- 但是,当出现错误时,使用 provide/inject 进行调试可能会更具挑战性。在包含大量不同提供者的大型应用程序中,这种挑战会更加突出(缺点)。
provide/inject 模式最适合应用程序范围的客户端数据,例如主题信息、区域设置/语言偏好和用户身份验证详细信息。这些类型的数据最好使用 provide/inject 来管理,因为应用程序中的任何组件都可能在任何给定时间需要访问它们。
另一方面,props 非常适合将数据隔离到一组特定的组件中。