- provide/inject类似发布/订阅通知,主要作用是在爷孙组件之间传递信息(父子也可以接收到,但还是建议使用props)
- provide发布的数据也是响应式的,但是在TS中如果不做显式类型限制将会被视为unknow,比如我们要订阅一个color数据,应该写成这种形式:
显式指定inject的数据类型
import type { Ref } from 'vue'
let colorVal = inject<Ref<string>>('color') // 这样colorVal的类型会变为Ref<string> | undefined
- 简单的发布——订阅系统,修改颜色。注:Vue3针对css做了优化,在css中可以直接用v-bind函数绑定脚本中的数据
App.vue(爷组件,发布者)
<template>
<div class="section">
<h1>App.vue(爷爷级别)</h1>
<div>
<!-- 靠name来约束,相同的name只有一个input能被激活 -->
<label><input v-model="colorVal" type="radio" name="color" value="red">红色</label>
<label><input v-model="colorVal" type="radio" name="color" value="green">绿色</label>
<label><input v-model="colorVal" type="radio" name="color" value="blue">蓝色</label>
<div class="background"></div>
</div>
</div>
<A></A>
<B></B>
</template>
<script setup lang="ts">
import { ref, reactive,provide,readonly } from 'vue'
import A from './components/A.vue'
import B from './components/B.vue';
import type { Ref } from 'vue';
let colorVal = ref<string>('red')
provide<Ref<string>>('color',readonly(colorVal))
</script>
<style scoped>
.section {
height: 190px;
border: 1px solid #ccc;
}
.background {
width: 50px;
height: 50px;
margin: 15px 0 0 10px;
// Vue3针对css的优化:可以用v-bind绑定脚本中的数据
background-color: v-bind(colorVal);
}
</style>
A.vue(父组件,订阅者)
<template>
<div class="section">
<h2>A.vue(父亲级别)</h2>
<div class="background"></div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, inject } from 'vue'
import type { Ref } from 'vue'
let colorVal = inject<Ref<string>>('color')
</script>
<style scoped>
.section {
height: 190px;
border: 1px solid #ccc;
}
.background {
width: 50px;
height: 50px;
margin: 15px 0 0 10px;
background-color: v-bind(colorVal);
}
</style>
B.vue(孙组件,订阅者)
<template>
<div class="section">
<h2>B.vue(孙子级别)</h2>
<div class="background"></div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, inject } from 'vue'
import type { Ref } from 'vue'
let colorVal = inject<Ref<string>>('color')
// 使用非空运算符!
colorVal!.value = 'blue'
</script>
<style scoped>
.section {
height: 190px;
border: 1px solid #ccc;
}
.background {
width: 50px;
height: 50px;
margin: 15px 0 0 10px;
background-color: v-bind(colorVal);
}
</style>
- 在父子组件中可以修改colorVal的值,并且这种改变会影响其他所有使用到这个值的组件(发布者也受影响),如果不想让父子组件修改可以引入一个readonly函数,在发布时使用即可
App.vue
<script setup lang="ts">
import { ref, reactive,provide,readonly } from 'vue'
import A from './components/A.vue'
import B from './components/B.vue';
import type { Ref } from 'vue';
let colorVal = ref<string>('red')
// 在发布时写上readonly(发布内容)即可,这样后代组件尝试修改时vue会报警告
provide<Ref<string>>('color',readonly(colorVal))
</script>
- colorVal如果为undefined则可选链操作符将不起作用,这时可以使用!非空运算符或设置默认值来规避掉undefined的问题
B.vue(孙组件)
// 错误示范
<script setup lang="ts">
import { ref, reactive, inject } from 'vue'
import type { Ref } from 'vue'
let colorVal:Ref<string>|undefined = inject<Ref<string>>('color')
// 爆红,可选链操作符不可用
colorVal?.value = 'blue'
</script>
// 解决方案1:非空运算符!
<script setup lang="ts">
import { ref, reactive, inject } from 'vue'
import type { Ref } from 'vue'
let colorVal = inject<Ref<string>>('color')
// 使用非空运算符!
colorVal!.value = 'blue'
</script>
// 解决方案2:设置默认值
<script setup lang="ts">
import { ref, reactive, inject } from 'vue'
import type { Ref } from 'vue'
// 设置默认值
let colorVal = inject<Ref<string>>('color',ref('red'))
colorVal.value = 'blue'
</script>