【vue3】provide/inject API

2,501 阅读4分钟

👋欢迎评论,点赞,收藏哦~
😗要是有不对的地方,欢迎指正哦~

provide/inject是什么呢

在进入正文前,先讲一个真实的故事,是我N年前读到的,虽然故事的名字,主人翁和背景我都忘记了,但是一点不影响我在这陈述它😜😜😜

故事是这样的,Z家族在英国的地位非常的显赫,家族中也是人才辈出,家族里有一条规定,那就是每个人都要为后代留下一句话,话不论长短,不论对错,只要是真心的感悟,今生的沉淀都可以。在他们家族里,有一条长长的走廊,挂着祖祖辈辈留下来的人生格言,这些话都是宝贵的精神财富,就这样一代一代的传下去,影响着一批批的后人。

说到这,可以把Z家族类比成Vue组件树,每个人留下来的话,类比成provide,其后代都可以访问到。那provide就相当于一个数据提供者,其后代都有权限访问到这些数据,后代访问时通过inject API来获取,要注意的是provide和inject必须应用在一棵树上, 也就是说,从inject的节点往上回溯,得能找到与其对应的provide才行。

image.png

相比于props传值,得一层层从父级传到子级,provide/inject的方式就方便的多,可以系统的、跨越层级的传递数据,provide来提供依赖,inject来使用依赖,组件之间的接口定义仍然是明确的,而且

  • 父组件不需要知道哪些子组件使用它 provide 的 property
  • 子组件不需要知道 inject 的 property 来自哪里

provide/inject可以处理响应性的数据啦~

这相比Vue2中的provde/inject强大许多,因为在Vue2中它们是不拥有响应性的,

// App.vue
export default {
    provide() {
        return {
            checkout_info: this.checkout_info,
            updateCheckoutInfo: this.updateCheckoutInfo
        }
    },
    data() {
        return {
            checkout_info: {},
        }
    },
    methods: {
        updateCheckoutInfo(key, value) {
            this.checkout_info[key] = value
        }
    }
}

// SomeComponent.vue
export default {
    inject: ['checkout_info', 'updateCheckoutInfo']
    computed: {
        deliveryAddress: {
            get() { return this.checkout_info.delivery_address }, // <---- Not reactive
            set(value) { return this.updateCheckoutInfo('delivery_address', value) }
        }
    }
}

要想变成响应性,手动使用了Object.defineProperty,当触发getter时,获取checkout_info当前最新的值,如下,

export default {
    data() {
        return {
            checkout_info: {},
        }
    },
    provide() {
        const appData = {}

        Object.defineProperty(appData, "checkout_info", {
            enumerable: true,
            get: () => this.checkout_info,
        })

        return {
            updateCheckoutInfo: this.updateCheckoutInfo,
            appData,
        }
    }
}

Referencestackoverflow.com/questions/6…


Vue3中借助reactivity API,可以传递和操作响应性数据,举个例子👀: image.png

当provide提供响应性数据,inject去改变这个响应性数据时,就打破了Vue中单项数据流的限制,会造成数据流向不清晰,debug困难,想一想,哪一个inject都能直接改动provide的数据,追踪变化就变得十分困难。

所以,官方就建议,对响应式 property 的所有修改限制在定义 provide 的组件内部,意思就是provide不仅提供响应性数据,还要提供方法来负责改变响应式property,inject中不直接变动数据。

举个例子👀:

image.png image.png Reference: v3.vuejs.org/guide/compo…

除此之外,利用readonly包裹响应性数据也很重要哦,这样对应响应式对象就只读了,而且还是深度的作用在每个property上。 image.png

与composition API结合

我们也可以在组合式 API 中使用 provide/inject。两者都只能在当前活动实例的 setup() 期间调用。

先看一个例子,这里定义了一个composable方法,里面包含了一些api请求,这些请求以provide的形式向其所在的组件树提供接口,组件树中的组件以inject的方式使用。

👉举这个例子,想说的是,借助composition API可以把业务逻辑抽离到单独的js文件中维护,借助provide/inject API可以实现状态共享依赖注入的功能。

image.png

image.png

image.png

与vuex对比

写到这,发现provide/inject结合composition可以做好多事,比如依赖注入,状态共享,处理响应性,逻辑复用,那这么看vuex能做的,provide/inject也能做。

举个简单的例子,看看provide/inject如何替代vuex的👀:

image.png

image.png

image.png Referencevuejsdevelopers.com/2020/10/05/…

我觉得最重要的一点是,vuex是全局单例的模式,意味着所有的数据是全局共享的,举个例子,a1组件实例和a2组件实例都是由A组件的构造函数生成的,那这俩实例共享一个vuex的state,也就是说,这个state状态不会以组件的维度去保存数据。要是非要一个组件实例维护一个state状态,你只能从数据上做文章,如下所示:

image.png

但是provide/inject可以做到局部多例,可以向一棵组件树提供不同的provide/inject数据 ( 以provide第一个参数name为区分), 也可以向不同的组件树提供数据。

image.png

如图所示,在同一个Vue根实例里,provide/inject可以以局部多列的模式存在(一类颜色代表一种依赖注入) image.png

小结👉: image.png