依赖注入:provide 和 inject
什么情况下推荐provide/inject使用:Prop 多层级数据透传
通常情况下,当我们需要从父组件向子组件传递数据时,会使用 props。想象一下这样的结构:有一些多层级嵌套的组件,形成了一棵巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据。在这种情况下,如果仅使用 props 则必须将其沿着组件链逐级传递下去,这会非常麻烦:
上图方式,数据传递,只会一级一级的向下传递,如果组件链路非常长,可能会影响到更多这条路上的组件。这一问题被称为“prop 逐级透传”,显然是我们希望尽量避免的情况
provide 和 inject 可以帮助我们解决这一问题
一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖
Provide (提供)
provide() 函數接收兩個參數 provide(注入名,注入值)
<script setup>
import { provide } from 'vue'
provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
//例子
provide('message', 'hello!')
</script>
第一個參數被稱為注入名,可以是一個字符串或是一個Symbol。後代組件會用注入名來查找期望注入的值。一个組件可以多次调用 provide(),使用不同的注入名,注入不同的依赖值。
第二個參數是提供的值,值可以是任意类型,包括响应式,比如一個 ref
import { ref, provide } from 'vue'
const count = ref(0)
provide('key', count)
provide全局注入
// main.ts页面
import { createApp } from 'vue'
const app = createApp({})
app.provide('message', 'hello!') //全局注入一个message数据
//所有組件中都可以获取到注入的message数据
使用 Symbol 作注入名
什么情况下使用 Symbol 作为注入名:大型的应用,包含非常多的依赖提供,或者你正在编写提供給其他开发者使用的组件库
const myInjectionKey = Symbol()
provide(myInjectionKey, { /*要提供的数据*/ });
const injected = inject(myInjectionKey)
Inject (注入)
获取 provide注入数据的方法
<script setup>
import { inject } from 'vue'
const message = inject('message')
console.log('message', message) //输出 hello!
</script>
注意:如果提供的值是一个 ref,注入进来的会是该 ref 对象,而不会自动解包为其內部的值(注入进来是什么类型,接收的就是什么类型)
inject 设置接收默认值
//如果注入的message为空的话 value会是默认的值 XXXXXX
const value = inject('message', 'XXXXXX')
在一些场景中,默认值可能需要通过调用一个函数或初始化一个类来取得
const value = inject('key', () => new ExpensiveClass(), true)
//第三个参数表示默认值应该被当作一个工厂函数
配合响应式数据使用
当提供 / 注入响应式的数据时,建议尽可能將任何对响应式状态的变更都保持在提供方組件中
<!-- 在供給方組件/父组件內 -->
import { provide, ref } from 'vue'
const location = ref('North Pole')
function updateLocation() {
location.value = 'South Pole'
}
provide('location', {
location,
updateLocation
})
<!-- 在注入方組件/子组件内 -->
<script setup>
import { inject } from 'vue'
const { location, updateLocation } = inject('location')
</script>
<template>
// 点击 updateLocation方法,可以调用父组件的updateLocation方法,从而更改 location 的值
<button @click="updateLocation">{{ location }}</button>
</template>
如果你想确保提供的数据不能被(注入方组件/子组件)修改,可以使用 readonly() 来包裝提供的值
// readonly() 只读
const count = ref(0)
provide('read-only-count', readonly(count))
具体参考: https://zh-hk.vuejs.org/guide/components/provide-inject.html#prop-drilling