Composition API Provide/Inject
本文假定你已经阅读了Provide/Inject,Composition API介绍和响应性基本原理。
我们在使用Composition API的时候,我们也可以使用provide/inject。二者都只能在setup()内部使用当前活动实例进行调用。
背景
假设我们需要重构下面这段代码,里面有一个MyMap组件,这个组件通过Composition API,向MyMarker子组件provide了用户的location。
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
provide: {
location: 'North Pole',
geolocation: {
longitude: 90,
latitude: 135
}
}
}
</script>
<!-- src/components/MyMarker.vue -->
<script>
export default {
inject: ['location', 'geolocation']
}
</script>
使用Provide
在setup()里面使用provide时,我们开始先要从vue里显式引入这个方法。它允许我们通过provide的调用定义每个属性。
provide方法允许你通过两个参数定义属性:
- 属性的名字(<String>类型)
- 属性的值
运用在我们的MyMap组件时,我们所provide的值可以像下面那样重构:
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import { provide } from 'vue'
import MyMarker from './MyMarker.vue
export default {
components: {
MyMarker
},
setup() {
provide('location', 'North Pole')
provide('geolocation', {
longitude: 90,
latitude: 135
})
}
}
</script>
使用Inject
在setup()里面使用inject时,我们也需要显式地从vue里引入它。引入了之后,我们就可以调用它来定义它的数据该如何暴露给我们的组件。
inject方法接受两个参数:
- 要inject的属性的名字
- 可选的默认值
运用在我们的MyMarker组件时,我们可以通过下面的代码重构它:
<!-- src/components/MyMarker.vue -->
<script>
import { inject } from 'vue'
export default {
setup() {
const userLocation = inject('location', 'The Universe')
const userGeolocation = inject('geolocation')
return {
userLocation,
userGeolocation
}
}
}
</script>
响应性
增加响应性
如果要在provide与inject的值之间增加响应性,我们可以在provide值的时候使用ref或者reactive。
运用在我们的MyMap组件时,我们的代码可以像下面那样重构:
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import MyMarker from './MyMarker.vue
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
provide('location', location)
provide('geolocation', geolocation)
}
}
</script>
现在,其中任意一个属性有改动时,MyMarker组件都会自动地更新。
修改响应式属性
在使用响应式的provide/inject的值时,推荐的做法是尽量将所有对响应式属性的修改都放在provider一侧。
例如,在某次事件中,我们要更改用户的location,理想情况下我们是在MyMap组件中进行修改。
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import MyMarker from './MyMarker.vue
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
provide('location', location)
provide('geolocation', geolocation)
return {
location
}
},
methods: {
updateLocation() {
this.location = 'South Pole'
}
}
}
</script>
然而,有时候我们需要在inject数据的组件内部对数据进行更新。在这种场景中,我们推荐provide一个方法,来负责修改这个响应式属性。
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import MyMarker from './MyMarker.vue
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = 'South Pole'
}
provide('location', location)
provide('geolocation', geolocation)
provide('updateLocation', updateLocation)
}
}
</script>
<!-- src/components/MyMarker.vue -->
<script>
import { inject } from 'vue'
export default {
setup() {
const userLocation = inject('location', 'The Universe')
const userGeolocation = inject('geolocation')
const updateUserLocation = inject('updateLocation')
return {
userLocation,
userGeolocation,
updateUserLocation
}
}
}
</script>
最后,如果你想确保provide的数据不会在inject数据的子组件中被修改,我们推荐使用readonly来provide属性。
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import { provide, reactive, readonly, ref } from 'vue'
import MyMarker from './MyMarker.vue
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = 'South Pole'
}
provide('location', readonly(location))
provide('geolocation', readonly(geolocation))
provide('updateLocation', updateLocation)
}
}
</script>