vue祖先与孙子跨级别传递方法(祖先调用孙子方法、中间层调用)-----provide、inject
单变量存储多个方法
// injectionKeys.js
// injectionKeys.js
export const DataIntegrationGrandApiKey = Symbol('DataIntegrationGrandApi');
祖先组件(API 管理者)
✔ 一个 provide
✔ 一个 key
✔ 清晰、安全
核心职责:
持有一个
apiRef提供
registerGrandApi把“容器对象” provide 出去
<script setup>
import { ref, provide } from 'vue';
import { DataIntegrationGrandApiKey } from '@/injectionKeys';
// 用来存孙子暴露的 api
const grandApiRef = ref(null);
// 孙子调用:注册 / 注销
function registerGrandApi(api) {
grandApiRef.value = api;
}
// ⚠️ 注意:provide 的是 ref,不是 .value
provide(DataIntegrationGrandApiKey, {
registerGrandApi, // 孙子用
grandApiRef, // 中间层 / 其他后代用
});
</script>
孙子组件(API 提供者)
✔ 孙子只关心注册
✔ 不关心谁调用
✔ 不依赖层级
核心职责:
实现真实业务方法
mounted 时注册
unmount 时注销
<script setup>
import { inject, onMounted, onBeforeUnmount } from 'vue';
import { DataIntegrationGrandApiKey } from '@/injectionKeys';
const apiContainer = inject(DataIntegrationGrandApiKey);
function save() {
console.log('孙子:save 被调用');
}
function insert(code) {
console.log('孙子:insert', code);
}
const api = {
save,
insert,
};
onMounted(() => {
apiContainer?.registerGrandApi(api);
});
onBeforeUnmount(() => {
apiContainer?.registerGrandApi(null);
});
</script>
中间层组件
核心职责:
直接调用孙子暴露的方法
不需要 emit / 不需要 ref 链
<script setup>
import { inject } from 'vue';
import { DataIntegrationGrandApiKey } from '@/injectionKeys';
const apiContainer = inject(DataIntegrationGrandApiKey);
function onClickSave() {
apiContainer?.grandApiRef.value?.save();
}
function onInsert() {
apiContainer?.grandApiRef.value?.insert?.('test code');
}
</script>
<template>
<button @click="onClickSave">保存</button>
<button @click="onInsert">插入</button>
</template>
如果后续代码需要等待,则使用promise或者await定义异步方法。主动停止js执行
-------------------------------------下方为简单示例及原理姐好啊---------------------------
1️⃣ 定义 Injection Key(单独文件)目的:避免方法名重复:
✅ Symbol 的优势(核心)
-
全局唯一(不会冲突)
-
类型系统可约束
-
IDE 自动提示
-
官方推荐(Vue / Pinia 都在用)
ts版本:
// injectionKeys.ts
import type { InjectionKey } from 'vue';
export interface GrandApi {
save: () => void;
insert?: (code: string) => void;
}
export const RegisterGrandApiKey: InjectionKey<
(api: GrandApi | null) => void
> = Symbol('RegisterGrandApi');
js版本:
// injectionKeys.ts:跨级方法调用
export const RegisterGrandApiKey = Symbol('RegisterGrandApi');
祖先组件(提供注册器)
<script setup lang="ts">
import { provide, ref } from 'vue';
import { RegisterGrandApiKey, type GrandApi } from '@/injectionKeys';
const grandApi = ref<GrandApi | null>(null);
function registerGrandApi(api: GrandApi | null) {
grandApi.value = api;
}
function callSave() {
grandApi.value?.save();
}
provide(RegisterGrandApiKey, registerGrandApi);
</script>
<template>
<el-button @click="callSave">保存</el-button>
<Parent />
</template>
孙子组件(注册能力)
<script setup lang="ts">
import { inject, onMounted, onBeforeUnmount } from 'vue';
import { RegisterGrandApiKey, type GrandApi } from '@/injectionKeys';
function save() {
console.log('孙子保存逻辑');
}
const api: GrandApi = {
save,
};
const registerGrandApi = inject(RegisterGrandApiKey);
onMounted(() => {
registerGrandApi?.(api);
});
onBeforeUnmount(() => {
registerGrandApi?.(null);
});
</script>
中间层使用孙子方法:
一句话工程总结(你可以直接记)
Symbol = 接口名
InjectionKey = 类型约束
provide / inject = 依赖注入
一、provide / inject 是干嘛的?
一句话定义:
用于祖先组件向任意层级的后代组件“依赖注入”数据或方法
👉 不受组件层级限制
对比一下你熟悉的方式
| 方式 | 能否祖先→孙子 | 是否强耦合 | 推荐程度 |
|---|---|---|---|
| props | ❌ | 高 | ⭐ |
| emit | ❌ | 高 | ⭐ |
| ref | ⚠️ | 极高 | ❌ |
| provide / inject | ✅ | 低 | ⭐⭐⭐⭐⭐ |
二、基本用法(最简单版)
祖先组件
import { provide } from 'vue';
provide('msg', 'hello');

浙公网安备 33010602011771号