[译] 用 Vue 3 Composition API 实现 React ContextProvider 模式

    技术2022-07-12  82

    原文:https://markus.oberlehner.net/blog/context-and-provider-pattern-with-the-vue-3-composition-api/

    React Context API 提供了一种 不用在组件树中逐层传递 props (也称 prop drilling)的前提下 共享被多个组件都需要的属性 (比如用户设置、UI 主题等)的方式。尽管 Vue.js 没有自带的完全一致的抽象,但在本文中,我们将看到 在 Vue 3 中,我们已经拥有了可以快速复刻前者功能的所有必需工具。

    用户设置 provider

    在本例中,来看看如何运用这一模式 让某些信息全局可用。

    如下的 ProvideUserSettings 组件,提供了一个反应式的 state 及一些默认值,还有一个 update() 函数用以设置 state 对象。

    // src/components/ProvideUserSettings.js import {   provide,   reactive,   readonly,   toRefs, } from 'vue'; // 使用 symbols 制造独特标识 export const UserSettingsStateSymbol = Symbol('User settings state provider identifier'); export const UserSettingsUpdateSymbol = Symbol('User settings update provider identifier'); export default {   setup() {     const state = reactive({       language: 'en',       theme: 'light',     });          // 使用 `toRefs()` 确保其在消费者组件中广泛可用     // 而 `readonly()` 预防了用户修改全局状态     provide(UserSettingsStateSymbol, toRefs(readonly(state)));     const update = (property, value) => {       state[property] = value;     };     provide(UserSettingsUpdateSymbol, update);   },   render() {     // 该 provider 组件是 “renderless” 的     // 其自身不渲染任何东西     return this.$slots.default();   }, };

    下面来看看如何在应用中使用 ProvideUserSettings 组件:

    <!-- src/App.vue --> <script> import ProvideUserSettings from './components/ProvideUserSettings'; export default {   name: 'App',   components: {     ProvideUserSettings,   }, }; </script> <template>   <ProvideUserSettings>     <div>       <!-- ... -->     </div>   </ProvideUserSettings> </template>

    或许在遍及应用各处的多个组件中都需要这个设置。因此,将 provider 置于顶层的 App 组件中很有必要。

    如此一来在组件树中的任意位置都能访问到该用户设置了。

    <!-- src/components/ButtonPrimary.vue --> <script> import { inject } from 'vue'; import { UserSettingsStateSymbol } from './ProvideUserSettings'; export default {   setup() {     const { theme } = inject(UserSettingsStateSymbol);     return { theme };   }, }; </script> <template>   <ButtonBase     :class="$style[`t-${theme}`]"   >     <slot/>   </ButtonBase> </template> <style module> .t-light { /* ... */ } .t-dark { /* ... */ } </style>

    如上所示,我们已经看到了如何在 inject() 过的上下文中 消费 用户设置状态 了。

    接下来的例子中,将演示如何在应用中的任意组件里 更新 该状态:

    <!-- src/components/ThemeSwitcher.vue --> <script> import { inject } from 'vue'; import { UserSettingsUpdateSymbol } from './ProvideUserSettings'; export default {   setup() {     const updateUserSettings = inject(UserSettingsUpdateSymbol);     const updateTheme = value => updateUserSettings('theme', value);     return { updateTheme };   }, }; </script> <template>   <div>     <button @click="updateTheme('dark')">       Enable darkmode     </button>     <button @click="updateTheme('light')">       Enable lightmode     </button>   </div> </template>

    这一次我们通过 UserSettingsUpdateSymbol 来 inject() 得到 provider 中的 update() 函数,并将其包裹在一个新的 updateTheme() 函数中,用来直接设置用户设置对象中的 theme 属性。

    当两个按钮之一被点击,用户设置就被更新了,并且 因为该状态是一个反应式对象,所有 inject() 了该状态的组件也都将被更新。

    --End--

    查看更多前端好文请搜索 fewelife 关注公众号转载请注明出处

    Processed: 0.033, SQL: 9