🧩 一、核心原理(简单讲人话)
在 Vue3 中:
-
provide 是父组件提供一个依赖值;
-
inject 是子组件接收这个依赖值;
-
默认情况下,provide 提供的是一个「普通的引用值」,而不是响应式的。
👉 这意味着:
如果你在父组件中 later(异步)修改了 provide 的值,而这个值不是响应式对象,那么子组件不会自动更新。
🧠 二、最简单示例:静态 provide(不响应)
js
<!-- App.vue -->
<template>
<div>
<h2>父组件</h2>
<button @click="changeName">修改名字</button>
<Child />
</div>
</template>
<script setup>
import { provide } from 'vue'
import Child from './Child.vue'
let username = '小明'
// 向子组件提供 username
provide('username', username)
function changeName() {
username = '小红'
console.log('父组件修改了 username =', username)
}
</script>
<!-- Child.vue -->
<template>
<div>
<h3>子组件</h3>
<p>用户名:{{ username }}</p>
</div>
</template>
<script setup>
import { inject } from 'vue'
const username = inject('username')
</script>
🧩 运行结果:
-
初始显示:用户名:小明
-
点击"修改名字"按钮后,子组件界面不会更新!
📖 原因:
因为 provide('username', username) 提供的是普通字符串,不具备响应式特性。
✅ 三、扩展版:让 provide 变成响应式的(推荐写法)
要让子组件能「自动响应父组件异步变化」,只需要用 ref 或 reactive 包装即可。
js
<!-- App.vue -->
<template>
<div>
<h2>父组件</h2>
<button @click="changeName">异步修改名字(2秒后)</button>
<Child />
</div>
</template>
<script setup>
import { ref, provide } from 'vue'
import Child from './Child.vue'
const username = ref('小明')
// ✅ 提供响应式的值
provide('username', username)
function changeName() {
setTimeout(() => {
username.value = '小红'
console.log('父组件异步修改 username = 小红')
}, 2000)
}
</script>
<!-- Child.vue -->
<template>
<div>
<h3>子组件</h3>
<p>用户名:{{ username }}</p>
</div>
</template>
<script setup>
import { inject } from 'vue'
const username = inject('username') // 自动响应
</script>
🧩 运行结果:
-
初始显示:用户名:小明
-
点击按钮后 2 秒 → 自动更新为:用户名:小红
✅ 因为我们注入的是 ref,Vue3 会自动处理 .value 的响应式绑定。
❌ 四、错误示例:异步 provide 失效的情况(常见坑)
有时新手会这么写:
js
<!-- App.vue -->
<template>
<div>
<h2>父组件</h2>
<button @click="loadData">异步加载 provide 值</button>
<Child />
</div>
</template>
<script setup>
import { provide, ref } from 'vue'
import Child from './Child.vue'
let user = null
function loadData() {
setTimeout(() => {
user = { name: '异步用户' }
provide('user', user) // ❌ 错误!在 setup 外部、异步中调用 provide 无效
console.log('异步 provide 完成')
}, 2000)
}
provide('user', user)
</script>
<!-- Child.vue -->
<template>
<div>
<p>子组件:{{ user }}</p>
</div>
</template>
<script setup>
import { inject } from 'vue'
const user = inject('user')
</script>
🧩 现象:
-
初始显示:子组件:null
-
点击"异步加载"后,依然不变!
📖 原因:
provide 只能在组件 setup() 执行时建立依赖关系,
异步调用 provide() 没有效果,Vue 根本不会重新建立依赖注入。
🔍 五、正确的异步写法总结
场景 | 错误示例 | 正确写法 |
---|---|---|
父组件 setup 后再异步修改 | 普通变量 | ✅ 使用 ref 或 reactive |
异步中重新调用 provide() | ❌ 无效 | ✅ 一次 provide 响应式引用即可 |
想实时共享对象状态 | ❌ 普通对象 | ✅ 用 reactive() 或 Pinia |
🧱 六、总结
类型 | 响应式 | 子组件会更新? | 推荐 |
---|---|---|---|
provide('a', 普通变量) | ❌ 否 | ❌ 否 | ❌ |
provide('a', ref()) | ✅ 是 | ✅ 是 | ✅ |
provide('a', reactive()) | ✅ 是 | ✅ 是 | ✅ |
异步重新调用 provide() | ❌ 无效 | ❌ 否 | ❌ |