非父子通信: 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 更轻量;但全局状态仍推荐官方状态管理库。

相关推荐
mayaairi1 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen2 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马2 小时前
AI源码分析:拆解模型底层实现逻辑
前端
BillKu3 小时前
全局样式变量:CSS 自定义属性(--border-color)和SCSS 变量($border-color)的说明
javascript·css·scss
IT_陈寒3 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端
乘风gg3 小时前
DeepSeek V4.1 Flash 来了,明天中午 Flash 降价 60%
前端·ai编程·claude
默_笙3 小时前
❗ 给金鱼装记忆(上):从白板到笔记本,AI 的短期记忆生存指南
前端·javascript
酷酷的逗逗乐3 小时前
前端转 Agent 开发 · 第五节:Memory 会话记忆
前端·程序员
掘金挖土4 小时前
前端手摸手跑路之 AI 应用开发(二)
前端·后端
兜里只有三分钱~4 小时前
翻卡_3D 的仪式感,和三个让它穿帮的写法
javascript·css·3d