非父子通信: provide和inject

一、核心概念

  1. 作用:允许祖先组件 (provider)向任意层级后代(injector)传递数据,而无需经过每一层 props 逐级下发或事件逐级上报。

  2. 语义:

    • provide:在祖先组件里"声明"要下发的数据。

    • inject:在后代组件里"声明"要接收的数据。

  3. 响应式差异:

    • Vue2 默认不具备响应式联动(provide 的值在运行时变了,后代不会自动更新)。

    • Vue3 只要 provide 的是 ref / reactive 对象,后代就能自动追踪变化。

二、Vue2 示例(Options API)

javascript 复制代码
<template>
  <div>
    <h3>根组件 (Vue2) 主题: {{theme}}</h3>
    <button @click="toggleTheme">切换主题</button>
    <hr>
    <Child/>
  </div>
</template>

<script>
import Child from './Child.vue'

export default {
  components: { Child },
  data() {
    return {
      theme: 'light'          // 普通字符串,非响应式
    }
  },
  provide() {                 // 关键点1:函数写法,才能访问 this
    return {
      theme: this.theme,      // 注意:仅初始化时快照,后续变化不会同步
      toggleTheme: this.toggleTheme
    }
  },
  methods: {
    toggleTheme() {
      this.theme = this.theme === 'light' ? 'dark' : 'light'
    }
  }
}
</script>

子孙组件

javascript 复制代码
<template>
  <div style="margin-left: 40px;">
    <p>孙子组件拿到主题:{{theme}}</p>
    <button @click="toggleTheme">孙子触发切换</button>
  </div>
</template>

<script>
export default {
  inject: ['theme', 'toggleTheme'],   // 关键点2:数组语法
  created() {
    console.log('Vue2 inject 到的数据:', this.theme)
  }
}
</script>

运行结果:

  • 初始能读到 theme;

  • 根组件按钮可切换,但孙子不会自动刷新(非响应式)。

三、Vue3 示例(Composition API)

javascript 复制代码
<template>
  <div>
    <h3>根组件 (Vue3) 主题: {{theme}}</h3>
    <button @click="toggleTheme">切换主题</button>
    <hr>
    <Child/>
  </div>
</template>

<script setup>
import { provide, ref, readonly } from 'vue'
import Child from './Child.vue'

const theme = ref('light')
function toggleTheme() {
  theme.value = theme.value === 'light' ? 'dark' : 'light'
}
// 关键点1:使用 provide(key, value)
provide('theme', readonly(theme))      // 只读保护,防止被随意修改
provide('toggleTheme', toggleTheme)
</script>

子孙组件

javascript 复制代码
<template>
  <div style="margin-left: 40px;">
    <p>孙子组件拿到主题:{{theme}}</p>
    <button @click="toggleTheme">孙子触发切换</button>
  </div>
</template>

<script setup>
import { inject } from 'vue'

const theme = inject('theme')            // 关键点2: inject(key, 默认值?)
const toggleTheme = inject('toggleTheme')

// 默认值示例
const user = inject('user', { name: 'Guest' })
</script>

运行结果:

  • 根组件切换按钮,孙子立即同步刷新(响应式)。

四、常见陷阱速查

  1. Vue2 中 provide 写成对象而非函数,导致访问不到实例属性。

  2. 把非响应式字面量(如普通字符串、数字)provide 出去,后期改动后代看不到。

  3. 直接修改 inject 拿到的响应式对象,会绕过单向数据流;最好 provide('xxx', readonly(xxx))

  4. 跨组件层级很深时,provide/inject 比 EventBus 或 Vuex/Pinia 更轻量;但全局状态仍推荐官方状态管理库。

相关推荐
马可菠萝几秒前
从零开始,用 Tauri + Vue 3 打造轻量级桌面应用
前端
陆枫Larry1 分钟前
JavaScript 字符串处理实战:从 `startsWith` 到链式 `replace` 的避坑指南
前端
Mr_li1 分钟前
给 Vue 开发者的 uni-app 快速指南
vue.js·uni-app
天蓝色的鱼鱼18 分钟前
你的项目真的需要SSR吗?还是只是你的简历需要?
前端·架构
颜酱34 分钟前
单调队列:滑动窗口极值问题的最优解(通用模板版)
javascript·后端·算法
恋猫de小郭1 小时前
移动端开发稳了?AI 目前还无法取代客户端开发,小红书的论文告诉你数据
前端·flutter·ai编程
文心快码BaiduComate1 小时前
百度云与光本位签署战略合作:用AI Agent 重构芯片研发流程
前端·人工智能·架构
闲云一鹤2 小时前
nginx 快速入门教程 - 写给前端的你
前端·nginx·前端工程化
QCY2 小时前
「完全理解」1 分钟实现自己的 Coding Agent
前端·agent·claude
一拳不是超人2 小时前
Electron主窗口弹框被WebContentView遮挡?独立WebContentView弹框方案详解!
前端·javascript·electron