Vue 3 组件通信实战:用 provide/inject 与 defineExpose 打通跨层级共享
test12026-09-1713 次阅读
props 与 emit 的边界
父子组件用 props 往下传、emit 往上抛足够清晰,但一旦跨三层以上,逐层透传会让中间组件变成"只负责转手"的空心层。Vue 3 的 provide/inject 与 defineExpose 正好补上这块短板。
provide/inject:祖先直接喂数据给后代
祖先组件用 provide 暴露一个响应式对象或方法,任意后代用 inject 直接取,不必经过中间层。适合主题、当前用户、表单上下文这类"全局但限定在子树内"的数据。
<!-- Ancestor.vue -->
<script setup>
import { provide, ref } from "vue";
const user = ref({ id: 1, name: "张三" });
provide("user", user); // 响应式,后代自动更新
provide("logout", () => { user.value = null; });
</script>
<!-- DeepChild.vue -->
<script setup>
import { inject } from "vue";
const user = inject("user");
const logout = inject("logout");
</script>
用 inject 默认值与只读包装防误改
provide 出去的可变引用,后代也能直接改,容易失控。对只该读取的配置,用 readonly 包一层,让修改在开发环境直接报错。
import { provide, readonly } from "vue";
provide("config", readonly({ theme: "dark", lang: "zh" }));
defineExpose:父组件主动调子组件方法
默认情况下 script setup 里的变量对外不可见。用 defineExpose 显式暴露,父组件通过模板 ref 拿到实例后调用,适合表单校验、弹窗开关这类命令式操作。
<!-- Modal.vue -->
<script setup>
import { ref } from "vue";
const visible = ref(false);
function open() { visible.value = true; }
function close() { visible.value = false; }
defineExpose({ open, close });
</script>
<!-- Parent.vue -->
<script setup>
import { ref } from "vue";
import Modal from "./Modal.vue";
const modal = ref(null);
const onOpen = () => modal.value?.open();
</script>
<template>
<button @click="onOpen">打开</button>
<Modal ref="modal" />
</template>
选型建议
- 逐层透传的 props 超过两层,就考虑 provide/inject 收敛到子树根。
- 命令式动作(开关、校验、聚焦)用 defineExpose + 模板 ref,比 emit 来回更直白。
- 需要跨整个应用共享的,交给 Pinia,别滥用 provide 造成隐式依赖。
- provide 的可变数据尽量明确写入方,避免多个后代各自改写同一引用。
T
test1
文章作者
props 与 emit 的边界 父子组件用 props 往下传、emit 往上抛足够清晰,但一旦跨三层以上,逐层透传...
- 分类
- 技术
- 发布时间
- 2026-09-17
- 字数
- 约 1684 字
- 阅读
- 13 次
