一、前言
Provide与Inject函数能解决隔代传值问题。一个父组件使用Provide函数构造的对象,任何后代的组件树(无论层级有多深),都可以通过Inject函数注入后使用。- 适用场景:
一些不方便.不想用.懒得用vuex和pnina的场景 注意想要后代响应数据时,需提供响应式数据,解构不影响响应性provide(注入名,值)
二、代码注释详解
1、main.js
- 知识点:通过provide全局提供方法给任意组件使用
import App from './App.vue';
import { createApp } from 'vue';
const app = createApp(App);
// // 全局提供函数获取年龄阶段
app.provide('getStage', function getStage(age) {
if(age <= 6) return '童年';
else if(age > 6 && age <= 17) return '少年';
else if(age > 17 && age <= 40) return '青年';
else if(age > 40 && age <= 65) return '中年';
else if(age > 65) return '老年';
});
2、父组件及涉及知识点
- 知识点1:
provide提供响应式变量、固定值供任意后代组件使用 - 知识点2:
provide提供方法供任意后代组件使用 - 知识点3:
inject注入全局方法计算人生阶段
<template>
<div class="box">
父组件:
<div>输入姓名:<input type="text" v-model="name"></div>
<div>姓名--{{ name }} 年龄--{{ ageObj.age }} 人生阶段--{{ getStage(ageObj.age) }}</div>
<button @click="addAge">+1岁</button>
<Son></Son>
</div>
</template>
<script setup>
import Son from '@/components/son.vue';
import { ref, provide, inject } from 'vue';
// 有一个人叫张三,它也可以是任意一个人,后代也随他姓氏,
// 示例提供响应式变量
const name = ref('张三');
// 今年年龄88
// 示例提供对象
const ageObj = ref({
age: 88
});
// 性别男
// 示例提供固定值
const sex = '男';
// 加一岁
function addAge() {
ageObj.value.age++;
}
// 给后代组件提供自己的信息
// 此处注意数据提供时使用name/ageObj 而不是name.value/ageObj.value.age 否则会丢失响应性
// 通过reactive toRefs解构的值也同上理
// window.location.href = "https://juejin.cn/user/84036866547575"
// console.log(name,'响应数据');
// console.log(name.value,'仅值');
provide(/* 注入名 */ 'provideData',/* 值 */ {
// 当前人姓名
name, //当前简写形式 完整为name:name
// 当前人年龄
ageObj, //当前简写形式 完整为ageObj:ageObj
// 性别
sex, //当前简写形式 完整为sex:sex
// 年龄+1
addAge //当前简写形式 完整为addAge:addAge
});
// 获取全局年龄阶段函数
const getStage = inject('getStage');
</script>
<style scoped lang="scss">
.box {background-color: #cccccc;margin: 0 10%;}
</style>
3、子组件
- 知识点1:inject解构数据使用
- 知识点2:inject使用全局方法
<template>
<div class="son-box">
<div>这是子组件</div>
<!--入赘李家 改姓李-->
<!--父亲比儿子大三十岁 计算孙子年龄-->
<div>姓名--{{ '李入赘' }} 年龄--{{ ageObj.age - 30 }} 人生阶段--{{ getStage(ageObj.age - 30) }}</div>
<!--给子传姓氏-->
<Grandson surname="李"></Grandson>
</div>
</template>
<script setup>
import Grandson from '@/components/grandson.vue';
import { inject } from "vue";
// 拿父级组件的信息
// 解构不影响响应性
const { ageObj } = inject('provideData');
// 获取全局年龄阶段函数
const getStage = inject('getStage');
</script>
<style scoped lang="scss">
.son-box {background-color: pink;margin: 10px 12%;}
</style>
4、孙组件
- 知识点:
与3、子组件相同
<template>
<div class="grandson-box">
这是孙组件
<!--爷爷比孙子大三十岁 计算孙子年龄-->
<div>姓名--{{ props.surname + '子' }} 年龄--{{ ageObj.age - 50 }} 人生阶段--{{ getStage(ageObj.age - 50) }}</div>
<Offspring></Offspring>
</div>
</template>
<script setup>
import Offspring from '@/components/offspring.vue';
import { inject, defineProps } from 'vue';
// 取父姓
const props = defineProps(['surname']);
// 拿祖宗组件的信息
const { ageObj } = inject('provideData');
// 获取全局年龄阶段函数
const getStage = inject('getStage');
</script>
<style scoped lang="scss">
.grandson-box {background-color: aqua;margin: 0 14%;}
</style>
5、后代组件
- 知识点1:
与3、子组件相同 - 知识点2:
祖先组件与后代组件的事件联动
<template>
<div class="offspring-box">
这是任意后代组件
<!--三代还宗 后代用祖姓继续命名-->
<!--window.location.href = "https://juejin.cn/user/84036866547575"-->
<!--祖宗比后代大八十岁 计算后代年龄-->
<div>姓名--{{ name ? name[0] + '后代' : '后代' }} 年龄--{{ ageObj.age - 80 }} 人生阶段--{{ getStage(ageObj.age - 80) }}</div>
<button @click="addAge">+1岁</button>
</div>
</template>
<script setup>
import { inject } from 'vue';
// 拿祖宗级组件的信息
const { name, ageObj, addAge } = inject('provideData');
// 获取全局年龄阶段函数
const getStage = inject('getStage');
</script>
<style scoped lang="scss">
.offspring-box {background-color: yellowgreen;margin: 0 16%;}
</style>
三、补充信息
1、补充知识点
禁用后代修改祖提供的数据
- 当provide函数传的值是引用型数据时,可以使用readonly函数对值进行包装;经readonly函数包装的值,在后代使用时禁止可写,强行写会报警。
- 基本类型或基本类型变量不能使用readonly函数包装,系统会报错。
- ref变量会失效(即使他的value值是引用型数据),请用reactive构建响应性函数而不要用ref去构建。
- 书写格式:provide('注入名', readonly(值))
- 注意:provide与readonly要先用import从库中导入。
import { ref, provide, readonly } from 'vue';
const name = ref({ n: '张三' });
provide('name', readonly(name));
2、注意事项
1、传值机制
邻代传值(透传、Props、v-model)需要把变量赋在父标签的属性上,属性再赋给子变量。
隔代传值(Provide)变量不需借助父标签上的属性,直接赋值给子组件的变量。
2、父子响应性
引用型变量: 无论是邻代还是隔代传值,父子都可互相响应更新。
基本类型变量: 邻代传值子组件只可读,子组件能借助父标签,利用连带更新手动响应父(单向响应)。
隔代传值子组件可写,但互不响应,因不能借助父标签,不能利用连带更新(其它响应性变量更新)手动响应父。
3、关于ref
邻代传值: ref在父标签上会自动解包,传值是value属性而不是ref对象,当value值是基本类型时,子变量沦为基本类型,只能手动响应父,且子变量只读。
隔代传值 是整个ref对象赋给子变量。子变量继承父变量的ref,在子组件中使用需加上value。
4、生命周期
除DOM对象外父参在子组件初始化时即可使用。 在<template>使用,DOM对象需定义为ref响应性变量且变量要赋空对象(避免‘无法读取未定义属性’警告),注ref变量不要使用readonly函数包装,否则失去相互响应性。在<template>使用在``下引用需放在onBeforeUpdate生命函数之上的函数体内。