一、provide/inject是啥
- provide/inject用于【祖组件】和【后代组件】之间的传值,有点像加强版的props
- 在【祖组件】中定义provide传递值
- 在【后代组件】中通过inject接收值
- 一般在开发中比较少用,适用于组件的开发
- 和props相比,不需要一层一层地在子组件标签上定义props;和$parent相比,组件跨级时获取【祖组件】中的值比较方便
watch: {
'$parent.$parent.count': {
handler(val) {
console.log(val)
}
}
}
- 如果【祖组件】和【父组件】都传递了provide并且都是count,那么【子组件】中取【父组件】的count,就近原则
- 如果【祖组件】的provide传递了count,【父组件】的provide传递了count1,在【子组件】中可以将inject写成对象形式,通过from属性明确当前值取自哪个provide
inject: {
count: { from: 'count' }
}
二、实现一个简易的祖孙组件之间的传值
祖组件(App.vue)
<template>
<div id="app">
<p>{{count}}</p>
<el-button @click="handleAdd">+</el-button>
<Parent />
</div>
</template>
<script>
import Parent from './components/Parent'
export default {
name: 'app',
data() {
return { count: 0 }
},
provide() {
return { count: this.count }
},
methods: {
handleAdd() {
this.count = ++this.count
}
},
components: { Parent }
}
</script>
父组件(Parent.vue)
<template>
<div class="parent">
父组件
<p>{{count}}</p>
<Child />
</div>
</template>
<script>
import Child from './Child'
export default {
inject: ['count'],
components: { Child }
}
</script>
子组件(Child.vue)
<template>
<div class="child">
子组件
<p>{{count}}</p>
</div>
</template>
<script>
export default { inject: ['count'] }
</script>
此时,【祖组件】中的count已经传递到【后代组件】中了,并且可以正常渲染。
但是,更新【祖组件】中的count时,后代组件不能够动态的更新。
三、响应式传值
provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
①修改provide
provide() {
return {
count: () => this.count // 这里不要用解构赋值 const { count } = this; return { count }
}
}
②后代组件获取值时,count是一个函数,加上()
<!-- <p>{{count}}</p> -->
<p>{{count()}}</p>
此时,便可以实现响应式传值
其他方式
另外,provide传递了一个对象时,也是响应式的,即改变祖组件时孙组件会一起改变。但是当孙组件改变对象的属性时,祖组件也会跟着变,破坏了单项数据流原则,这会造成数据变化不可控
当然了,在inject中取到对象时,在props/data/computed/watch/created中用另一个变量接收深复制后的对象,但祖组件再次改变对象中的属性时,孙组件中不再跟着变化了
四、provide/inject可以定义成对象的结构
provide以对象形式定义,这种方式拓展性没有函数形式好,如果没有响应式可以采取这种方式传递参数(不推荐)
provide: {
count: '100'
}
inject以对象形式定义,可以对provide中的参数重命名和设置默认值,需要时可以采取这种方式接收参数(推荐)
inject: {
newCount: { from: 'count', default: 100 }
}
from的值,要和父组件/祖组件中provide提供的对象名称对应
五、后代组件如何改变【祖组件】中的provide
【祖组件】的provide中传入addCount
provide() {
return {
count: () => this.count
addCount: () => (this.count = this.count + 1)
}
}
【后代组件】接收addCount
inject: ['count', 'addCount']
并触发addCount
<el-button @click="addCount()">addCount</el-button>