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

相关推荐
java1234_小锋5 小时前
【技术专题】HTML5 网页技术 - HTML5 Canvas 基础以及动画入门
前端·html·html5
知了学历孙老师8 小时前
小自考的校考和统考,出题方和难度差在哪
java·大数据·前端
ShineWinsu10 小时前
对于JavaScript:操作元素的解析
开发语言·前端·javascript·面试·web·dom·元素
福兮说11 小时前
JSON 格式正确,数据就可靠吗?我用一份自编配置检查类型与空值
java·前端·json
程序员老赵13 小时前
Docker 部署青龙面板:轻松搭建定时任务管理平台
前端·docker·开源
Swot13 小时前
Asciidoctor 公式数字下标错位问题:原因分析与解决方案
前端
Swot13 小时前
leo-editor 中将 Markdown 内容转成 asciidoc 格式
前端
狒狒AI13 小时前
Remotion / GSAP+Three / HyperFrames / Skia 四栈实测:代码渲染知识视频的选栈方法(附开源技能)
前端·人工智能·aigc
修炼的dance13 小时前
浏览器里导出H.265,8.5秒和55.5秒差在硬件
前端
补码缩写13 小时前
WebCodecs选编码器,探测之后还要验解码和播放
前端