Vue组件通信

156 阅读1分钟

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

组件间通信方式

常用传参

props (组件间传参)

vuex (统一状态管理)

eventBus (中央事件总线)

自定义事件 ($emit,$on

不常用传参

  • 边界情况
    • $parent
    • $children
    • $root
    • $ref
    • provide/inject
  • 非props
    • $attrs
    • $listeners

实操

eventBus

class bus{
  constructor(){
    this.callbacks = {}
  }
  $on(name,fn){
    this.callbacks[name] = this.callbacks[name] || []
    this.callbacks[name].push(fn)
  }
  $emit(name,ages){
    if(this.callbacks[name]) this.callbacks[name].forEach(cb => cb(ages))
  }
}
vue.prototype.$bus = new bus()

$children

不能保证顺序

// 父元素
<template>
    <div id="home">
        <child />
        <button @click="goHome">回家吃饭</button>
    </div>
</template>
<script>
import child from './components/child.vue'
export default{
    compoents:{child},
    methods:{
        goHome(){
            // 调用child内的eat方法
            this.$children[0].eat()
        }
    }
}
</script>

$attrs

<template>
// 父元素直接传入msg,不使用props接收
<div>{{$attrs.msg}}</div>
</template>

$listeners

调用父元素方法 父组件

<template>
  <div class="home">
    <child2 msg="这里是Home" @click="onClick"/>
  </div>
</template>

<script>
import child2 from '@/components/child2.vue'
export default {
  name: 'Home',
  components: {
      child2
  },
  methods:{
    onClick(){
      console.log('Home方法',this)
    }
  }
}
</script>

子组件

<template>
    <!--触发父元素的方法,点击打印的this在父组件中-->
    <button v-on="$listeners">click</button>
</template>

provide/inject

高阶组件库

深层次传递,只要上层级元素有,底层元素无论在哪里都能接收到,通用组件库开发时可用(element Ui,中十分常用)

上层组件

<script>
    export default{
        provide:{
            return {
                foo:'fooooo'
            }
        }
    }
</script>

底层组件

    export default {
        inject:['foo']
        inject:{bar:{from:'foo'}}
        
    }

小Demo

// 父组件
<template>
	<a-vue @cut="cut" :name="name"></a-vue>
</template>
<script setup>
// 在这种语法糖下,不用将做额外处理,就可以使用
import AVue from 'xxx/Avue.vue'
import {ref} from 'vue'
let name = ref('韩梅梅')
const cutName = (e) =>{
  console.log(e)
  switch(name.value){
    case '韩梅梅':
      name.value = 'rousi'
      break
    case 'rousi':
      name.value = '韩梅梅'
      break
 }
}
</script>
// 子组件 AVue
<template>
<a-component :name=“name” @cut=“emits('cut')"></a-component>
</template>
<scrip setup>
import AComponent from 'xxx/AComponent.vue'
// 一般在vue3中选择用一个变量接住defineProps传来的方法
// 在版本比较新的vue3中不用引入props和emits方法就可以直接使用
let props = defineProps({
    name:String
})
defineProps({
    name:String
})
let emits = defineEmits([‘cut’])
</scrip>
// 孙组件 AComponent
<template>
<div>{{props.name}}</div>
<input type="submit" @click="emits" >
</template>
<script setup>
const props = defineProps({
    name:String
})
const emits = defomeEmits(['cut'])
</script>

这样还只是两层的props,如果更深入呢

我们可以选择provide/inject 配合computed(计算属性使用)

// 父组件
<template>
	<a-vue @cut="cut" :name="name"></a-vue>
</template>
<script setup>
// 在这种语法糖下,不用将做额外处理,就可以使用
import AVue from 'xxx/Avue.vue'
import {ref} from 'vue'
let name = ref('韩梅梅')
const cutName = (e) =>{
  console.log(e)
  switch(name.value){
    case '韩梅梅':
      name.value = 'rousi'
      break
    case 'rousi':
      name.value = '韩梅梅'
      break
 }
}
</script>
// 子组件 AVue
<template>
// 不在使用 : @ 的方式引入
<a-component></a-component>
</template>
<scrip setup>
import AComponent from 'xxx/AComponent.vue'
import {computed,provide} from 'vue'
// 使用解构,给script内部使用
let {name} = defineProps({
    name:String
})
let emits = defineEmits([‘cut’])
// 使用计算属性将值改变为响应式
let name = computed(()=>name)
// 将其注入
provide('name',name)
</scrip>
// 孙组件 AComponent
<template>
<div>{{name}}</div>
<input type="submit" @click="cut" >
</template>
<script setup>
// 这里需要使用inject,将其引用
import {inject} from 'vue'
// 这里注入的方法就和平时一样正常去使用
const cut = inject('cut')
const name = inject('name')
</script>