原文:markus.oberlehner.net/blog/contex…
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() 了该状态的组件也都将被更新。