1. 提出问题
Vue是基于组件化的框架,在组件之间进行通信是很常见的需求,父组件向子组件通信方式有非常多,但是祖先组件向后代组件通信的需求却没有那么多的技术方案。这时候Vue自带的provide和inject API就可以派上用场了。但是在使用provide和inject时,可能会出现一些问题。比如,在一个组件中使用provide,但是在另一个组件中却无法正确获取到provide中的数据等等。
2. 什么是provide和inject
provide和inject是Vue2.2.0新增的API,用于祖先组件向后代组件进行数据传递。
provide选项允许我们指定我们想要提供给后代组件的数据。在组件中,我们可以使用inject选项来引入这个数据。provide和inject在组件树中祖先和后代之间进行了双向绑定,具体的表现形式有点类似于prop和$emit。
provide选项
provide选项用于在一个组件中将数据提供给它的所有后代组件。provide选项应该是一个对象或返回一个对象的函数。这些提供的数据可以是任意类型的,包括基本类型、函数、对象、数组等等。
// 父组件提供数据
provide: {
name: 'John',
age: 18,
sayHello: function () {
console.log('Hello, I am John.')
}
}
inject选项
inject选项用于从祖先组件接收数据。它应该是一个字符串数组或一个对象。 字符串数组代表了inject的key,而对象则允许你以key: value
的形式进行配置。使用inject选项可以让我们在任何后代组件中都可以访问到提供的数据.
// 子组件获取数据
inject: ['name', 'age', 'sayHello']
使用provide和inject实现祖先组件通信
使用provide和inject进行祖先组件向后代组件通信的步骤如下:
1. 在祖先组件中使用provide选项,提供需要传递的数据;
2. 在后代组件中使用inject选项,声明需要接收哪些数据;
3. 在后代组件中使用接收到的数据。
比如,在祖先组件A中提供数据,后代组件B中接收数据并使用:
// 祖先组件A提供数据
provide: {
message: 'hello from ancestor',
showMessage: function () {
alert(this.message)
}
}
// 后代组件B接收数据
inject: ['message', 'showMessage']
// 后代组件B使用数据
mounted () {
alert(this.message)
this.showMessage()
}
3. 提出问题的场景及原因
在我们的实际业务中,可以使用provide和inject来处理祖先组件向后代组件的通信,这样可以避免通过props一层层的逐层传递数据。使用provide和inject确实可以让代码更加整洁,但是在使用过程中会有一些问题。
问题场景:在组件A中使用provide提供一个字符串类型的变量给组件B。在组件B中使用inject获取该变量并赋值给本地的一个data属性,但页面显示的是undefined。
// 祖先组件A提供数据
provide: {
message: 'hello from ancestor'
}
// 后代组件B接收数据
inject: ['message'],
data () {
return {
// 将message赋值给本地的一个data属性值
myMessage: this.message
}
}
第一眼看上去是没有问题,但是实际使用时会发现页面上显示的是undefined而不是我们期望的字符串。造成这个错误的原因在于 provide 组件中的数据只有在其子孙组件中被使用时才会被激活。也就是说,provide选项并不是一路向下传递的,而是在依赖关系中动态绑定的。
因此,在这个问题中,组件A提供的数据在后代组件B中并没有被激活。而我们传递的是一个简单类型的变量,Vue在这种情况下基于JS引用的复制机制会将这个简单类型的变量复制到后代组件中,丢失original message的来源。这个时候接收到的变量就是undefined,并不能得到组件A中提供的message值。
4. 解决方案
为了解决上述问题,需要使用一个新的对象来存储provide的值,这个新的对象不能是简单类型,而必须是一个包含我们需要提供的数据的对象。
// 祖先组件A提供一个包含了message数据的对象
provide: {
myOriginalMessage: {
message: 'hello from ancestor'
}
}
// 后代组件B接收数据
inject: ['myOriginalMessage'],
data () {
return {
// 将message从接收到的对象中取出来并赋值给本地的一个data属性值
myMessage: this.myOriginalMessage.message
}
},
现在,我们将message数据存储在一个包含该数据的对象当中,并在provide选项中提供这个对象。这样就可以将provide数据激活在后代组件当中,并从这个对象中取出需要的数据进行使用了。
5. 总结
使用provide和inject可以实现祖先组件向后代组件进行通信,提高了代码的可读性和可维护性。但是在使用时需要注意传递的是简单类型的数据还是对象,以及provide的数据只有在其子孙组件中被使用时才会被激活的问题。
提出问题、查找原因、解决问题是一个非常典型的解决问题方式,将其运用到开发中也是非常有意义的,可以帮助我们快速定位问题并给出有效的解决方案。使用Vue,我们也可以通过阅读文档和实践来逐步掌握其使用技巧和常见问题的处理方法。