这可能是最全的vue通信教程了
一,父子组件通信
最常见的父子组件通信模式
1.the-parent 父组件传递一个count 给子组件,子组件中做点击事件后传递回来
<template>
<div class="the-parent">
<the-child @change="change" :count="count"></the-child>
</div>
</template>
<script>
export default {
data () {
return {
count: 1
}
},
methods: {
change () {
this.count++
}
}
}
</script>
2.子组件拿到count 值,点击后执行 ++ 并通知给父组件
<template>
<div class="the-child">
<button @click="click"> {{ count }} </button>
</div>
</template>
<script>
export default {
name: 'the-child',
props: {
count: {
type: Number
}
},
methods: {
click () {
// this.count++ 此处不能直接操作 vue 组件的prop !!
// 只能通过事件传递在父组件中他自己处理他的数据
this.$emit('change')
}
}
}
</script>
这里补充思考一下此处场景的几种优化方案
1.双向绑定法
<template>
<div class="the-child">
<button @click="click"> {{ count }} </button>
</div>
</template>
<script>
export default {
name: 'the-child',
props: {
count: {
type: Number
}
},
model:{
prop: 'count',
}
methods: {
click () {
// input 为双向绑定默认的事件名
this.$emit('input', this.count + 1)
}
}
}
</script>
父组件中只需要通过 v-model 将count 传递给子组件就行
<template>
<div class="the-parent">
<the-child v-model="count"></the-child>
</div>
</template>
<script>
export default {
data () {
return {
count: 1
}
},
methods: {
}
}
</script>
这种方法再第一种标准父子通信的基础上优化省略的很多代码,但是无疑增加了一个 v-model 的理解成本,下面介绍另一种方案
2.sync + update:[propName]
在父组件中传递props的属性上以 .sync 进行修饰,表示这个属性可以在子组件中同步修复
<template>
<div class="the-parent">
<the-child :count.sync="count"></the-child>
</div>
</template>
<script>
export default {
data () {
return {
count: 1
}
},
methods: {
}
}
</script>
在子组件中使用$emit('updata:count', newVal) 这个时候父组件中不用接受事件,就可以实现父组件data自动同步
<template>
<div class="the-child">
<button @click="click"> {{ count }} </button>
</div>
</template>
<script>
export default {
name: 'the-child',
props: {
count: {
type: Number
}
},
methods: {
click () {
// updata:[propName]
this.$emit('update:count', this.count + 1)
}
}
}
</script>
二,直属兄弟组件通信
1.父组件中有两个同级兄弟组件的通信
<template>
<div class="the-parent">
<child-one></child-one>
<child-two></child-two>
</div>
</template>
2.可以采用父亲委托中介法
Child-one.vue
<template>
<div class="child-one">
<button @click="click">
点我通信child-two
</button>
</div>
</template>
<script>
export default {
methods: {
click () {
this.$parent.$emit('childOneClick', "传递的数据")
}
}
}
</script>
Chile-two.vue
<template>
<div class="child-one">
<button @click="click">
点我通信child-two
</button>
</div>
</template>
<script>
export default {
methods:{
handler(msg){
console.log(msg)
}
},
created(){
this.$parent.$on('childOneClick',this.handler)
}
}
</script>
// 此中父亲委托中介的通信方式,可以越过中间层,直接完成兄弟之间的通信。而且后续很多传值都是采用此方式
三,多层父子级别通信方式(注入通信)
先看看这个场景
the-a组件
<template>
<div>
<the-b></the-b>
</div>
</template>
The-b组件
<template>
<div>
<the-c></the-c>
</div>
</template>
The-c组件
<template>
<div>
<button>
点我触发事件
</button>
</div>
</template>
我们的需求是需要c组件和a组件通信,这里首先不考虑其他方式,只能一级一级的 c传b,b再传a,实际场景中可能级别更多。这里我们可以使用注入通信的方式
我们改造一下a 组件 和 c 组件
The-A 组件
<template>
<div>
<the-b></the-b>
</div>
</template>
<script>
export default {
provide: function () {
return {
theA: this // 此处将自己的实例作为provide 传递
}
},
methods:{
handler(msg){
console.log(msg)
}
},
created(){
this.$on('childClick',this.handler)
}
}
</script>
the-c
<template>
<div>
<button @click="click">
点我触发事件
</button>
</div>
</template>
<script>
export default {
inject:['theA'],
methods:{
click(){
if(this.theA){
// 此处触发
this.theA.$emiit('childClick', '我是要传递的数据')
}
}
}
}
</script>
// 通过上面的改造即可实现C --> A 的通信,而且通过$on 注册的事件会被记录在组件自己的events中,组件销毁时会自动销毁事件。
这里得引申一下,此种方式传值在设计递归,树形传值时,大量使用
如下
tree-root // 树根
tree-node // 子节点
tree-node // 子子节点
tree-node
在上面这样的情况,树形是根据数据动态渲染出来的,编写代码也是递归组件。
这个时候可以通过上面的 provide tree-root 然后在tree-node 中去inject 的方式委托传值。
四,vue根实例通信
这种方式其实是上面注入传值的一种思考。
上面的注入是我们根据需求手动注入的,其实vue早就有帮我们提供一个属性,就是我们的$root 根实例,
我们在复杂的通信的时候,往往比较棘手,如果使用上面的注入方式,无疑得知道 A,B 组件的公共祖先,
其实我们忽略了,$root 一直都是所有组件的公共祖先。
A组件
this.$root.$on('test',(msg){
// ...
})
B组件
this.$root.$emit('test', '通过$root传递')
总结:
万变不离其中,我们只要明白vue 的通信方式本质是通过$on 和 $emit 来实现的就不难理解上面的通信方。
这里可能有小伙伴会说到:buys-空实例通信,这里我并不推荐,1是需要额外实例一个vue实例,2,是通过空实例通信,注册的事件务必需要销毁,否则会造成内存泄漏。
也会有小伙伴想到vuex, 这里阐述一下自己的看法,vuex 是一个公共的状态管理,更像是一个公共的响应式仓库,对于很多场景 碧如购物车这样的非常适用,但是有时候我们只是要点击按钮 触发列表刷新这样的通信事件,就不太适用,总不能为了通信一个刷新事件去加个state 然后通过改变 和 watch的方式去实现通信吧。另外值得一提的是,vuex的数据类似于全局变量,过多的使用而不去回收,也会对内存造成负担
后续补充..........