【Vue3】provide/inject用法以及原理(持续更新)
简介
provide可以向所有子孙组件提供数据以及提供修改数据的方法,子孙组件用inject使用数据。
使用方法
我们用一个例子来介绍provide, inject的使用。父组件的info, data数据传递给子组件以及孙子组件,同时父组件传递一个可以更新数据的方法。
父组件:
<template>
<div>我是父组件 {{info}}
<son></son>
</div>
</template>
<script>
import { defineComponent, provide, reactive, ref } from 'vue'
import son from './son.vue'
export default defineComponent({
setup() {
let info = ref(123); // 注意这里响应式的必须是ref
let datas = reactive({ // 这里这里复杂的数据的响应式必须是reactive
width: 100,
height: 50,
bg: 'pink'
});
const updateInfo = (val) => {
info.value = val; // 注意这里,必须是info.value, 要不然数据不会改变
}
provide('info', info);
provide('datas', datas)
provide("updateInfo", updateInfo);
return {
info
}
},
components: {
son
}
})
</script>
子组件:
<template>
<div>我是子组件 {{info}}
<grand-son></grand-son>
</div>
</template>
<script>
import { defineComponent, inject } from 'vue'
import grandSon from './grandSon.vue';
export default defineComponent({
setup() {
const info = inject('info');
return {
info
}
},
components: {
grandSon
}
})
</script>
孙子组件:
<template>
<div>我是孙子组件 <button @click="updateInfo('456')">点击更新info</button>{{info}} <br />宽度{{datas.width}}, 高度:{{datas.height}}, 背景: {{datas.bg}}</div>
</template>
<script>
import { defineComponent , inject, watch} from 'vue'
export default defineComponent({
setup() {
const info = inject('info');
const updateInfo = inject('updateInfo');
const datas = inject('datas');
watch(info, (val) => {
console.log('变了');
console.log(val);
});
return {
info,
datas,
updateInfo
}
},
})
</script>
注意点
**inject() can only be used inside setup() or functional components. **
inject()只能放在setup()生命周期里运行,不能放在别的周期里运行,也不能放在事件周期里运行。
1、这是允许的,因为console.log是setup()生命周期里的同步代码。
function xx() {
console.log(inject("abc"))
}
xx()
2、这是禁止的,因为setTimeout是异步函数,执行console.log(inject("abc"))的时候已经不在setup()生命周期里。
function xx() {
setTimeout(() => {
console.log(inject("abc"))
})
}
xx()
3、让例一的xx函数作为鼠标事件回调,也是禁止的,原因也一样。
4、放在Promise.then()也是禁止的,比如:
Promise.resolve().then(() => {
console.log(inject("abc"))
})
readonly
如果要确保通过 provide 传递的数据不会被注入的组件更改,可以对提供者的 property 使用 readonly。
比如父组件中的 let info = ref(123); 修改为info = readonly(123);
ref和reactive的区别
ref:由传入值返回一个响应式的、可变的且只有value一个属性的ref对象。
当ref被作为render context被返回,在template中使用该ref对象时,自动获取内部的值,不需要使用.value属性。
reactive:reactive的作用是将对象包装成响应式对象——通过 Proxy代理后的对象。
如果只使用 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
toRefs API 用来上面的问题——它将响应式对象的每个 property 都转成了相应的 ref。
function useMousePosition() {
const pos = reactive({
x: 0,
y: 0,
})
// ...
return toRefs(pos)
}
// x & y 现在是 ref 形式,可以i解构了!
const { x, y } = useMousePosition()