这可能是最全的vue通信教程了

271 阅读4分钟

这可能是最全的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的数据类似于全局变量,过多的使用而不去回收,也会对内存造成负担

​ 后续补充..........