从 React 到 Vue:在 Vue3 中 创建 createContext 优雅地实现依赖注入

您所在的位置:网站首页 vue3的provide 从 React 到 Vue:在 Vue3 中 创建 createContext 优雅地实现依赖注入

从 React 到 Vue:在 Vue3 中 创建 createContext 优雅地实现依赖注入

2024-07-15 23:12| 来源: 网络整理| 查看: 265

前言

vue的 provide/inject 也是一种实现组件之间依赖注入的方式,但是他也会存在一些痛点。

依赖注入层级关系不明确。提供的值可以被任何后代组件访问到。这种方式虽然提高了组件的复用性,但是也使得依赖注入的层级关系变得不明确,如果组件的依赖注入关系过于复杂,会导致代码的可读性和可维护性下降。 数据流向不够明显。 后台组件可以直接访问到提供的值,但是无法判断这些值是从哪里来的。这种方式可能会导致组件之间的耦合度增加,不利于组件的复用和维护。 不支持类型检查。 provide/inject 提供的方法和注入的方法类型割裂,只能用于泛型约束无法通过provide 时进行类型推导,导致类型容易错乱。

下面我实现了一个方法来规避这些问题。具体请看 createContext实现

createContext 实现

先来说一下 createContext 是用来做什么的?

createContext 能够让你创建一个 context 以便组件能够提供与读取局部层的数据。 直接说白一点就是 vue 中 provide/inject 的功能, 可以来完成提供与注入数据的工作

为了处理以上我们所描述的一些痛点,我基于 provide/inject 封装了一个类似于React的createContext方法来优雅地实现依赖注入,具体步骤如下:

在vue项目中使用 provide/inject 来实现依赖注入。 创建一个 createContext 方法,该方法返回一个对象包含了 Provider Consumer useContext。 在 Provider 组件中,将提供的值存储在组件的 provide 中。 在 Consumer 组件中,使用 inject 来获取提供的值。 在 useContext 方法中,返回了ComputedRef 对应的 value 是 inject 获取提供的值。 在使用时,可以通过 Provider 提供需要共享的数据,通过 Consumer 来消费这些数据。 优势 更加灵活的数据流向。 createContext的Provider组件可以将数据传递给它的后代组件,而后代组件在不经过父组件的情况下直接访问到这些数据。这种方式可以避免props层层传递、emit事件层层发射 的问题,提高了组件的可复用性。 更加简介的使用方式。 通过使用useContext函数,组件可以直接访问到Provider 提供的值,而不需要使用Consumer组件来包裹子组件,这种方式可以减少组件的嵌套层次,提高了代码的可读和可维护性。

下面是代码实现部分:

核心代码 // context.ts import { defineComponent, provide, computed, inject, type PropType, type ComputedRef } from 'vue'; export function createContext(defaultValue: T) { const KEY = Symbol('CREATE_CONTEXT_KEY'); const Provider = defineComponent({ props: { value: { type: [Object, Number, String, Boolean, null, undefined, Function] as PropType, required: true, }, }, setup(props, ctx) { provide( KEY, computed(() => props.value || defaultValue), ); return () => ctx.slots.default?.(); }, }); const useContext = () => inject(KEY) || computed(() => defaultValue); const Consumer = defineComponent({ setup(props, ctx) { const value = useContext(); return () => ctx.slots.default?.(value.value); }, }); return { Provider, Consumer, useContext }; } 使用参考 createContext(defaultValue)

在任意组件外调用 createContext 来创建一个上下文

import {createContext} from './context.ts' const ThemeContext = createContext("light") 参数

defaultValue: 当包裹的组件没有提提供值时,就会使用该值作为默认的上下文,倘若你不需要指定任何默认值时,可以不传,默认上下文为 undefined, 该值用作于最后的备选数据,永远不会改变。

返回值

createContext 返回一个 context 对象

ThemeContext.Provider: 他是一个 DefineComponent,接收一个 value 参数,让你提供上下文的值给被他包裹的子组件。 ThemeContext.Consumer: 他是一个 DefineComponent, 他的默认插槽 default 会提供一个 value 供子组件使用。 ThemeContext.useContext: 他是一个方法,该方法返回是一个 ComputedRef 对应的值时 最近一层 Provider的值。 ThemeContext.Provider

使用上下文 Provider 包裹组件,来为里面所有的组件指定一个 context

template 写法 import { ref } from 'vue'; import { ThemeContext } from './ThemeContext'; import Page from './Page.vue'; const theme = ref('light'); jsx 写法 import { ref, defineComponent } from 'vue'; import { ThemeContext } from './ThemeContext'; import Page from './Page.vue'; export default defineComponent({ setup() { const theme = ref('light'); return () => ( ); }, }); Props

value: 该值为你想传递给所有处于这个 provider 内读取该 context 的组件,无论它们层级嵌套的有多深。都可以获取,该值你可以定义为任何类型。该 provider 内的组件可以通过调用 useContext() 来获取它上面最近的context provider 的值。相当于使用 vue3 中的 provide(key, value) 方法。

ThemeContext.Consumer

在useContext没有出来之前,这是一种很老的方法来读取上下文。

template 写法 import { ThemeContext } from './ThemeContext'; {{ theme }} jsx 写法 import { defineComponent } from 'vue'; import { ThemeContext } from './ThemeContext'; export default defineComponent({ setup() { return () => ( {{ default(theme) { return {theme}; }, }} ); }, }); ThemeContext.useContext template 写法 import { ThemeContext } from './ThemeContext'; const theme = ThemeContext.useContext(); {{ theme }} jsx 写法 import { defineComponent } from 'vue'; import { ThemeContext } from './ThemeContext'; export default defineComponent({ setup() { const theme = ThemeContext.useContext(); return () => {theme.value}; }, });

在以上示例中我基本演示了一下三种方法的使用场景, Provider 提供了一个 theme ,使用 Consumer 和 useContext 来消费这个值。 当后代组件使用这个值的时候,将会被 vue 进行响应依赖收集,那 theme 发生变化, 使用组件的 redner 就会重新渲染。

这种方式可以让我们根据 provide/inject 优雅地实现依赖注入,避免了组件之间的耦合,提高了代码的复用性。

总结

本文借鉴了 React 中 createContext api,并且在原有的基础上调整,使用 provide/inject 创作而成。 优雅地实现依赖注入可以提高代码的可读性和可维护性,避免了组件之间的耦合,提高了代码的复用性,在实际开发中,需要根据具体场景选择合适的依赖注入方式,避免出现问题。

最后感谢大家的阅读,希望本文对你在前端开发的学习和实践中有所帮助。继续保持好奇心,追求卓越,享受前端开发的旅程!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3