Vue的一些总结(二)组件通信

本文详细介绍了Vue组件间的通信方式,包括父传子props、子传父$emit、$children/$parent、provide/inject、ref/$refs、eventBus、Vuex以及localStorage/sessionStorage等,并通过实例解析了各种通信方式的适用场景及其优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

组件通信

在知道组件之间是怎么通信的之前,我们先要知道组件之间都存在什么样的关系,对症下药才是王道。

在这里A-B A-C B-D C-E之间是父子关系
B-C是兄弟关系
A-D A-E是隔代关系,也就是爷爷和孙子的关系
D-E是堂兄弟关系
针对这些关系,我们归类为两大类:

  1. 父子组件之间通信
  2. 非父子组件之间的通信(隔代关系、兄弟组件等等)
父传子props

父组件通过props属性向子组件传递数据,子组件通过props属性接收父组件的数据。

// father父组件
<template>
 <div class="father">
	 <son :articles="articleList"></son>
 </div>
</template>

<script>
import Son from './test/son.vue'
export default {
 name: 'Father',
 components: { Son},
 data() {
	 return {
		 articleList: ['one', 'two']
	 	}
	 }
}
</script>
// 子组件 Son.vue
<template>
 <div>
 <span v-for="(item, index) in articles" :key="index">{{item}}</span>
 </div>
</template>
<script>
export default {
 props: {
 	articles:{
		type:String,
		default:[]
		}
 	}
}
</script>
子传父$emit

子组件通过$emit发送一个自定义事件,当这个语句被执行的额时候,就会将数据传递给父组件,父组件通过v-on监听并接收这个数据。

对于$emit 我自己的理解是这样的: $emit绑定一个自定义事件, 当这个语句被执行时, 就会将参数arg传递给父组件,父组件通过v-on监听并接收参数。 通过一个例子,说明子组件如何向父组件传递数据。 在上个例子的基础上, 点击页面渲染出来的ariticle的item, 父组件中显示在数组中的下标
// 父组件中
<template>
 <div class="father">
	 <son :articles="articleList" @onEmitIndex="onEmitIndex"></son>
 <p>{{currentIndex}}</p>
 </div>
</template>
<script>
import son from './test/Son.vue'
export default {
 name: 'Father',
 components: { Son},
 data() {
	 return {
		 currentIndex: -1,
		 articleList: ['one', 'two']
	 }
 },
 methods: {
 onEmitIndex(idx) {
	 this.currentIndex = idx
	 }
 }
}
</script>
<template>
 <div class="son">
 <div v-for="(item, index) in articles" :key="index" @click="emitIndex(index)">{{item}}</div>
 </div>
</template>
<script>
export default {
 props: ['articles'],
 methods: {
 emitIndex(index) {
	 this.$emit('onEmitIndex', index)
	 }
  }
}
</script>
$children / $parent

通过parent和children拿到组件实例,通过实例访问组件的所有方法和属性。

//父组件
// 父组件中
<template>
  <div class="father">
    <div>{{msg}}</div>
    <son/>
    <button @click="changeSon">点击改变子组件值</button>
  </div>
</template>

<script>
import Sonfrom './test/Son.vue'
export default {
  name: 'Father',
  components: { Son},
  data() {
    return {
      msg: 'Welcome'
    }
  },

  methods: {
    changeA() {
      // 获取到子组件Son
      this.$children[0].messageA = 'this is new value'
    }
  }
}
</script>

// 子组件中
<template>
  <div class="son">
    <span>{{messageA}}</span>
    <p>获取父组件的值为:  {{parentVal}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      messageA: 'this is old'
    }
  },
  computed:{
    parentVal(){
      return this.$parent.msg;
    }
  }
}
</script>

要注意边界情况,如在#app上拿parent得到的是newVue()的实例,在这实例上再拿parent得到的是newVue()的实例,在这实例上再拿parent得到的是undefined,而在最底层的子组件拿children是个空数组。也要注意得到children是个空数组。也要注意得到parent和children的值不一样,children的值不一样,children 的值是数组,而$parent是个对象。

provide/inject

provide/ inject 是vue2.2.0新增的api, 简单来说就是父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。
注意:这里无论子组件嵌套有多深,只要调用了inject,那么就可以注入provide中的数据,而不局限于只能从当前父组件的props属性中取数据

这里有三个组件,GrandFather,Father,Son

//爷爷组件GrandFather
<template>
  <div>
	<father/>
  </div>
</template>

<script>
  import Father from '../components/test/father.vue'
  export default {
    name: "GrandFather",
    provide: {
      for: "demo"
    },
    components:{
      Father
    }
  }
</script>

//父组件
// B.vue

<template>
  <div>
    {{demo}}
    <son/>
  </div>
</template>

<script>
  import Father from '../components/test/father.vue'
  export default {
    name: "Father",
    inject: ['for'],
    data() {
      return {
        demo: this.for
      }
    },
    components: {
      Son
    }
  }
</script>

//子组件
<template>
  <div>
    {{demo}}
  </div>
</template>

<script>
  export default {
    name: "Son",
    inject: ['for'],
    data() {
      return {
        demo: this.for
      }
    }
  }
</script>

ref/$refs

如果放在普通的DOM元素上,引用指向的就是DOM元素。如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据。

//子组件 Son.vue
export default {
  data () {
    return {
      name: 'Son.js'
    }
  },
  methods: {
    sayHello () {
      console.log('hello')
    }
  }
}
// 父组件 Father.vue

<template>
  <son ref="comA"/>
</template>
<script>
  export default {
    mounted () {
      const comA = this.$refs.comA;
      console.log(comA.name);  // Son.js
      comA.sayHello();  // hello
    }
  }
</script>
eventBus

又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
但是当项目较大的时候,容易造成难以维护的灾难

  1. 初始化
    首先需要创建一个事件总线并将其导出, 以便其他模块可以使用或者监听它.
// event-bus.js

import Vue from 'vue'
export const EventBus = new Vue()
  1. 发送事件
    假设你有两个组件:additonNum和showNum,这两个组件可以是父子组件,也可以是兄弟组件,这里我们以兄弟组件为例子。
<template>
  <div>
    <show-num/>
    <addition-num/>
  </div>
</template>

<script>
import showNum from './showNum.vue'
import additionNum from './additionNum.vue'
export default {
  components: { showNum, additionNum }
}
</script>
// addtionNum.vue 中发送事件

<template>
  <div>
    <button @click="additionHandle">加法器</button>    
  </div>
</template>

<script>
import {EventBus} from './event-bus.js'
export default {
  data(){
    return{
      num:1
    }
  },

  methods:{
    additionHandle(){
      EventBus.$emit('addition', {
        num:this.num++
      })
    }
  }
}
</script>
// showNum.vue 中接收事件

<template>
  <div>计算和: {{count}}</div>
</template>

<script>
import { EventBus } from './event-bus.js'
export default {
  data() {
    return {
      count: 0
    }
  },

  mounted() {
    EventBus.$on('addition', param => {
      this.count = this.count + param.num;
    })
  }
}
</script>
vuex各个模块

请看这篇博客:https://blog.youkuaiyun.com/qq_45814762/article/details/107588877

localStorage / sessionStorage

详情链接:https://blog.youkuaiyun.com/qq_45814762/article/details/107816967
这种通信比较简单,但是数据和状态比较混乱不容易维护。
通过window.localStorage.getItem(key)获取数据
通过window.localStorage.setItem(key,value)存储数据
注意用JSON.parse() / JSON.stringify()做数据格式转换

localStorage / sessionStorage可以结合vuex, 实现数据的持久保存,同时使用vuex解决数据和状态混乱问题。

$attrs / $listeners

$attrs

包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 props时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件——在创建更高层次的组件时非常有用。

$listeners

包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过v-on=”$listeners”传入内部组件——在创建更高层次的组件时非常有用。

老规矩,拿例子说话
如最开始的一张图,爷爷GrandFather,爸爸Father,儿子Son
我们想要爷爷和儿子进行传值

  1. props可以,但是需要一级一级的传递
  2. 事件总线可以,但是当多人合作的时候不方便维护
  3. Vuex可以,但是当传递的数据量小的时候,又不划算。
  4. $attrs / $listeners

父亲组件Father.vue

<template>
	<div class="father">
		<son :a="1"/>
	</div>
</template>
import Son from './Son.vue'

儿子组件Son.vue

<template>
	<div class="son">
		son
		{{$attrs}}
	</div>
</template>

打印结果是:son{{ a : “1”}}

但是此时儿子组件Son的根元素上面也带有a=“1”
如果我们不需要这个特性的话,只需要在子组件Son上面添加一个属性

export default {
	inheritAttrs:false
}

如果我们想爷爷向父亲传递的值,儿子也能获取到,就需要使用到v-bind="$attrs"

爷爷组件GrandFather.vue

<template>
	<div class="grand-father">
		<father :a:'1'/>
	</div>
</template>

import Father from './father.vue'

父亲组件Father.vue

<template>
	<div class="father">
		father
		{{$attrs}}
		<son v-bind="$attrs" b="2"/>
	</div>
</template>
import Son from './son.vue'

儿子组件Son.vue

<template>
	<div class="son">
		son
		{{$sttrs}}
	</div>
</template>

可以看到Son组件已经获取到了GrandFather的值。

那么Son要想GrandFather提交事件,如果$emit一层一层提交也很麻烦,我们可以使用v-on=" $listeners"
Son.vue

<template>
	<div class="son">
		son
		{{$attrs}}
		<button @click="sonClick"></button>
	</div>
</template>

methods:{
	sonClick(){
		this.$emit('test2','test2')
	}
}

Father.vue

<template>
	<div>
		father
		{{$attrs}}
		<son v-bind="$attrs" b='2' v-on="$listeners"/>
	</div>
</template>
import Son  from './son.vue'

GrandFather.vue

<template>
	<div class="grand-father">
		<father :a='1' @test2="test2"/>
	</div>
</template>

import Father from './father.vue'

methods:{
	test2(value){
		console.log(value)
	}
}

这个时候GrandFather组件已经接收到了Son组件传递的事件了。

总结

父子组件通信:$props / $emit ; $parent / $children ; provide / inject ; ref / $refs ; $attrs / $listeners
兄弟组件之间:eventBus ; vuex
跨级通信:eventBus ; vuex ; peovide / inject ; $attrs / $listeners

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值