Vue 3 中的
Provide
和Inject
机制是专为跨层级传递数据而设计的,适用于祖先组件和后代组件之间的通信。与props
和emits
不同,Provide/Inject
可以跨越多个层级进行数据传递,而不需要逐层传递。
1. Provide
provide
是一个在祖先组件中提供数据的方法,它将数据传递给该组件的所有后代组件(不需要直接的父子关系)。provide
通常在组件的 setup
函数中使用。
2. Inject
inject
是在后代组件中获取祖先组件提供的数据的方法。inject
也是在 setup
函数中使用,后代组件通过 inject
来访问祖先组件的 provide
数据。
基本使用
步骤 1:在祖先组件中使用 provide
提供数据
祖先组件通过 provide
将数据提供给后代组件。
javascript
// Parent.vue
<template>
<Child />
</template>
<script>
import { provide } from 'vue';
import Child from './Child.vue';
export default {
components: { Child },
setup() {
// 提供数据
const message = "Hello from Parent!";
provide('message', message); // 'message' 是标识符,message 是提供的值
}
};
</script>
在上面的例子中,Parent
组件通过 provide
提供了一个名为 message
的数据,值是字符串 "Hello from Parent!"
。任何后代组件都可以通过 inject
来访问这个数据。
步骤 2:在后代组件中使用 inject
获取数据
后代组件通过 inject
来访问祖先组件提供的数据。
javascript
// Child.vue
<template>
<div>{{ message }}</div>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
const message = inject('message'); // 使用 inject 获取数据
return { message };
}
};
</script>
在上面的代码中,Child
组件通过 inject
获取祖先组件 Parent
提供的 message
数据,并将其渲染在模板中。
如何通过后代组件修改上级组件的值
在 Vue 中,provide
传递的数据是响应式的,但它只提供了只读数据。若要让后代组件修改上级组件的数据,可以通过 provide
传递一个可变的对象或函数来实现。这也可以用来在后代组件中修改祖先组件的状态。
1. 传递响应式数据
你可以通过 reactive
或 ref
将响应式数据传递给后代组件,这样后代组件就可以通过修改响应式数据来更改祖先组件的状态。
javascript
// Parent.vue
<template>
<Child />
<div>{{ message.text }}</div> <!-- 显示修改后的数据 -->
</template>
<script>
import { reactive, provide } from 'vue';
import Child from './Child.vue';
export default {
components: { Child },
setup() {
// 提供响应式对象
const message = reactive({ text: "Hello from Parent!" });
provide('message', message); // 提供响应式对象
return { message };
}
};
</script>
在 Parent
组件中,使用 reactive
创建了一个响应式对象 message
,并通过 provide
提供给后代组件。接下来,后代组件就可以修改这个对象的内容。
2. 在后代组件中修改响应式数据
后代组件可以直接修改由 provide
提供的响应式对象的数据, 因为这违背了组件通信中单向数据流的规范,所以不推荐直接在后代组件中直接修改来自上级数据的值。
javascript
// Child.vue
<template>
<button @click="changeMessage">Change Message</button>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
const message = inject('message'); // 获取祖先组件提供的响应式对象
// 修改 message 对象中的值
const changeMessage = () => {
message.text = "Updated message from Child!";
};
return { message, changeMessage };
}
};
</script>
在 Child
组件中,通过 inject
获取祖先组件提供的响应式对象 message
,并提供一个方法 changeMessage
来修改 message.text
。由于 message
是响应式的,修改后会自动更新祖先组件中的视图。
通过函数传递数据
除了传递响应式对象外,另一个常见的方法是传递修改数据的函数。这样可以控制后代组件如何修改上级组件的值。
1. 祖先组件传递函数
javascript
// Parent.vue
<template>
<Child />
<div>{{ message }}</div> <!-- 显示修改后的数据 -->
</template>
<script>
import { ref, provide } from 'vue';
import Child from './Child.vue';
export default {
components: { Child },
setup() {
const message = ref("Hello from Parent!");
// 提供修改数据的函数
const changeMessage = (newMessage) => {
message.value = newMessage;
};
provide('changeMessage', changeMessage); // 提供修改函数
return { message };
}
};
</script>
在 Parent
组件中,我们通过 provide
提供了一个函数 changeMessage
,它接收一个新的消息并修改 message
的值。
2. 后代组件调用函数修改数据
javascript
// Child.vue
<template>
<button @click="changeParentMessage">Change Parent Message</button>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
const changeMessage = inject('changeMessage'); // 获取修改数据的函数
// 调用函数修改父组件的值
const changeParentMessage = () => {
changeMessage("Message updated from Child!");
};
return { changeParentMessage };
}
};
</script>
在 Child
组件中,通过 inject
获取 changeMessage
函数,并在按钮点击时调用它来修改父组件的 message
。
总结
provide
:在祖先组件中提供数据,供后代组件使用。inject
:在后代组件中接收祖先组件提供的数据。- 响应式数据传递 :你可以传递响应式数据对象(如
reactive
或ref
),使得后代组件修改这些数据会反映到祖先组件。 - 函数传递:通过提供修改函数,后代组件可以控制数据的修改,从而影响祖先组件的状态。
Provide/Inject
机制非常适合跨越多个层级的组件通信,并且能够避免通过多层级的props
传递,减少了组件间的耦合度。