[vue3] provide - inject
vue3快release了,一些新特性也需要了解下,在多层组件传递参数情况下,provide是更好的选择
组合式提供与注入
在组合式 API 中使用 provide/inject。两者都只能在当前活动实例的 `setup() 期间调用。
在 setup() 中使用 provide 时,我们首先从 vue 显式导入 provide 方法。这使我们能够调用 provide 时来定义每个 property。
一个例子如下:
<template>
<MyMarker />
</template>
<script>
import MyMarker from '../components/MyMarker'
import {provide} from 'vue'
export default {
components: {
MyMarker
},
setup() {
provide('location', 'North Pole')
provide('geolocation', {
longitude: 90,
latitude: 135
})
},
}
</script>
组合式 inject
在 setup() 中使用 inject 时,还需要从 vue 显式导入它。一旦我们这样做了,我们就可以调用它来定义如何将它暴露给我们的组件。
一个例子如下:
<template>
<div>{{ location }} - {{ geolocation }}</div>
</template>
<!-- src/components/MyMarker.vue -->
<script>
import { inject } from 'vue'
export default {
name: 'MyMarker',
setup() {
const location = inject('location')
const geolocation = inject('geolocation')
return {
location,
geolocation
}
}
}
</script>
响应式
为了增加提供值和注入值之间的响应性,我们可以在提供值时使用 ref 或 reactive。
使用 TemplateM
组件,我们的代码可以更新如下:
<template>
<MyMarker />
</template>
<script>
import MyMarker from '../components/MyMarker'
import {provide, ref, reactive} from '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>
现在,如果这两个 property 中有任何更改,MyMarker 组件也将自动更新!
Ref这种写法简单,但也有弊端,经过尝试,我发现他只能监听一些如数字、字符串、布尔之类的简单数据而如果需要监听如上面代码一样的jsonArray我们在vue2种都会使用$set来进行变更,到了vue3我们终于可以愉快的使用reactive来实现了。
修改响应式 property
当使用响应式提供/注入值时,建议尽可能,在提供者内保持响应式 property 的任何更改。
例如,在需要更改用户位置的情况下,我们最好在 TemplateM 组件中执行此操作。
<template>
<MyMarker />
</template>
<script>
import MyMarker from '../components/MyMarker'
import {provide, ref, reactive} from '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 = "hahaha"
}
}
}
</script>
然而,有时我们需要在注入数据的组件内部更新注入的数据。在这种情况下,我们建议提供一个方法来负责改变响应式 property,可以把函数updateLocation传给子组件并在子组件中调用。
<template>
<MyMarker />
</template>
<script>
import MyMarker from '../components/MyMarker'
import {provide, ref, reactive} from 'vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = "hahaha"
}
provide('location', location)
provide('geolocation', geolocation)
provide('updateLocation', updateLocation)
},
}
</script>
<template>
<div>{{ location }} - {{ geolocation }}</div>
</template>
<!-- src/components/MyMarker.vue -->
<script>
import { inject } from 'vue'
export default {
name: 'MyMarker',
setup() {
const location = inject('location')
const geolocation = inject('geolocation')
const updateLocation = inject('updateLocation')
return {
location,
geolocation,
updateLocation
}
}
}
</script>
最后,如果要确保通过 provide 传递的数据不会被注入的组件更改,我们建议对提供者的 property 使用 readonly。
<template>
<MyMarker />
</template>
<script>
import MyMarker from '../components/MyMarker'
import {provide, ref, reactive, readonly} from 'vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = "hahaha"
}
provide('location', readonly(location))
provide('geolocation', readonly(geolocation))
provide('updateLocation', updateLocation)
},
}
</script>
Ref与Reactive区别
- reactive:reactive的作用是将对象包装成响应式对象——通过 Proxy代理后的对象。
- ref:由传入值返回一个响应式的、可变的且只有value一个属性的ref对象。
当ref被作为render context被返回,在template中使用该ref对象时,自动获取内部的值,不需要使用.value属性。
可以通过如何撰写标准的 JavaScript 逻辑来比较:
// 风格 1: 将变量分离
let x = 0
let y = 0
function updatePosition(e) {
x = e.pageX
y = e.pageY
}
// 风格 2: 单个对象
const pos = {
x: 0,
y: 0,
}
function updatePosition(e) {
pos.x = e.pageX
pos.y = e.pageY
}
可以将风格 (1) 转换为使用 ref写法 (为了让基础类型值具有响应性) 。
将风格 (2) 转换为使用 reactive 对象的写法。
如果只使用 reactive 的问题是,使用组合函数时必须始终保持对这个所返回对象的引用以保持响应性。这个对象不能被解构或展开:
// 组合函数:
function useMousePosition() {
const pos = reactive({
x: 0,
y: 0,
})
// ...
return pos
}
// 消费组件
export default {
setup() {
// 这里会丢失响应性!
const { x, y } = useMousePosition()
return {
x,
y,
}
// 这里会丢失响应性!
return {
...useMousePosition(),
}
// 这是保持响应性的唯一办法!
// 你必须返回 `pos` 本身,并按 `pos.x` 和 `pos.y` 的方式在模板中引用 x 和 y。
return {
pos: useMousePosition(),
}
},
}
toRefs API 用来提供解决此约束的办法——它将响应式对象的每个 property 都转成了相应的 ref。
function useMousePosition() {
const pos = reactive({
x: 0,
y: 0,
})
// ...
return toRefs(pos)
}
// x & y 现在是 ref 形式,可以i解构了!
const { x, y } = useMousePosition()