vue隔代传值provide和inject

361 阅读5分钟

一、前言

  • ProvideInject函数能解决隔代传值问题。一个父组件使用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生命函数之上的函数体内。