provide/inject

provide(提供)和 inject(注入)也可以在组合式 API 的 setup 方法中使用,以实现跨越层级的组件通信。

provide 方法接收两个参数,一个是提供数据的 key;另一个是值 value,也可以是对象、方法等,如示例代码 4-7-1 所示。

示例代码 4-7-1 provide 方法
<div id="app">
    <component-b />
</div>
Vue.createApp({
    components: {
        'component-b': componentB
    },
    setup() {
        Vue.provide('location', 'North Pole')
        Vue.provide('geolocation', {
            longitude: 90,
            latitude: 135
        })
    }
}).mount("#app")

inject 方法接收两个参数,一个是需要注入的数据的 key,另一个是默认值(可选),如示例代码 4-7-2 所示。

示例代码 4-7-2 inject 方法
const componentB = {
    template:'<div>{{userLocation}}</div>',
    setup() {
        const userLocation = Vue.inject('location', 'The Universe')
        const userGeolocation = Vue.inject('geolocation')
        console.log(userGeolocation)
        return {
            userLocation,
            userGeolocation
        }
    },
}

和之前的配置式 API 不同的是,我们可以在 provide 值时使用 refreactive 方法,来增加 provide 值和 inject 值之间的响应性。这样,当 provide 的数据发生变化时,inject 也能实时接收到,如示例代码 4-7-3 所示。

示例代码 4-7-3 响应式 provide 数据
<div id="app">
    <component-b></component-b>
</div>

const componentB = {
    template:'<div>{{userLocation}}</div>',
    setup() {
        const userLocation = Vue.inject('location', 'The Universe')
        const userGeolocation = Vue.inject('geolocation')

        console.log(userGeolocation)

        return {
            userLocation,
            userGeolocation
        }
    },
}
Vue.createApp({
    components: {
        'component-b': componentB
    },
    setup() {
        const location = Vue.ref('North Pole')
        const geolocation = Vue.reactive({
            longitude: 90,
            latitude: 135
        })

        Vue.provide('location', location)
        Vue.provide('geolocation', geolocation)

        setTimeout(() => {
            location.value = 'China'
        },1000)
    },

}).mount("#app")

通常情况下,只允许在 provide 的组件内去修改响应式的 provide 数据,但是如果需要在被 inject 的组件内去修改 provide 的值,则需要 provide 一个回调方法,然后在被 inject 的组件内调用,如示例代码4-7-4所示。

示例代码 4-7-4 响应式 provide 数据
<div id="app">
    <component-b></component-b>
</div>
const componentB = {
    template:'<div>{{userLocation}}</div>',
    setup() {
        const userLocation = Vue.inject('location', 'The Universe')
        const updateLocation = Vue.inject('updateLocation')

        setTimeout(() => {
            updateLocation('China')
        },1000)

        return {
            userLocation,
        }
    },
}
Vue.createApp({
    components: {
        'component-b': componentB
    },
    setup() {
        const location = Vue.ref('North Pole')

        const updateLocation = (v) => {
            location.value = v
        }

        Vue.provide('location', location)
        Vue.provide('updateLocation', updateLocation)

    },

}).mount("#app")

最后,如果要确保通过 provide 传递的数据不会被 inject 的组件更改,则可以使用 readonly 方法,代码如下:

const location = Vue.ref('North Pole')
Vue.provide('location', Vue.readonly(location))