前言:
之前在 Vue 中进行组件通信一般都会使用 props,开始使用 provide/inject 是非常偶然的一次尝试。
当时在开发中需要实现祖孙组件,甚至祖祖祖祖孙组件之间的通信,在这种多层级场景下,props 就显得太过累赘了,由于是进行设计器(插件)开发,为了提高插件的可复用性,减少不必要的包依赖,倾向于在不引入 Vuex 的情况下解决这个问题,那么就应该看看 vue 本身,是否具有这种能力,就在这个时候,我发现了 provide/inject。
provide 可以在祖先组件中指定我们想要提供给后代组件的数据或方法,而在任何后代组件中,我们都可以使用 inject 来接收 provide 提供的数据或方法。
举个栗子
注:如果想要访问组件实例的属性的话 需要已函数的方式进行传递
// 父级组件提供 'foo'
<template>
<div>
<div>{{foo}}</div>
<son></son>
</div>
</template>
<script>
import Son from "./Son";
export default {
name: "parent",
components: { Son },
provide() { //函数方式传递 直接传递 provide:{foo:"测试"}
return {
foo: this.foo
};
},
data() {
return {
foo: "测试",
};
},
mounted() {
console.log(this.foo)
},
};
</script>
//子级组件,不接收
<template>
<grandSon></grandSon>
</template>
<script>
import grandSon from "./grandSon";
export default {
name: "son",
components: { grandSon },
};
</script>
//孙级组件,接收foo
<template>
<div>{{foo}}</div>
</template>
<script>
export default {
name: "grandSon",
inject: ["foo"],
mounted() {
console.log(this.foo)
},
};
</script>
在这里我们可以发现孙组件越过子组件接收了父组件注入的数据,我们可以理解为爷爷越过爸爸偷偷给孙子买了冰激凌,这是一组最简单的用法,当层级继续增加时,仍可通过这种方式由父组件直接跨域多个层级向后代组件注入数据。
有一点需要特别注意的是,实际上我们可以将当前组件inject获取的数据直接赋值给它本身的data或props,不过官网提示我们,这是在Vue2.2.1版本才实现的功能,在这之前,必须先进行props和data数据的初始化。
注:provide和inject并不是响应式的
在尝试中我们发现,由于Vue的单向流关系,实际上如果在parent中改变了初始传入的foo的值以后,grandSon并不会得到改变后的值,也就是在这个时候,父孙组件的数据出现了不一致的情况,我们肯定是希望拿到的数据是一致的,怎么来解决这个问题呢,官网还有一句提示为我们提供了解释。
提示: provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。 也就是指,我们需要人为的将这组数据关系变成可响应的,哦,我们之前的foo是一个字符串,基本数据类型是不具有响应特性的,那么,我们可能需要传递一个对象。
// 改造一下父级组件提供 'foo'
<template>
<div>
<div>{{foo}}</div>
<son></son>
</div>
</template>
<script>
import Son from "./Son";
export default {
name: "parent",
components: { Son },
provide() {
return {
foo: this.foo
};
},
data() {
return {
foo: {
foo: "测试"
},,
};
},
mounted() {
console.log(this.foo)
},
};
</script>
在这个栗子中,改造了一下父组件提供的数据,测试发现,foo变成了一组可响应的数据。经过尝试我发现在这种情况下如果在孙组件改变inject中foo的值,也会响应的更新到父组件中,当然为了保护单向数据流机制,最佳实践还是不要在子组件里更改inject(虽然 sync 也破坏了单向数据流)。