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

在这里A-B A-C B-D C-E之间是父子关系
B-C是兄弟关系
A-D A-E是隔代关系,也就是爷爷和孙子的关系
D-E是堂兄弟关系
针对这些关系,我们归类为两大类:
- 父子组件之间通信
- 非父子组件之间的通信(隔代关系、兄弟组件等等)
父传子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中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
但是当项目较大的时候,容易造成难以维护的灾难
- 初始化
首先需要创建一个事件总线并将其导出, 以便其他模块可以使用或者监听它.
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()
- 发送事件
假设你有两个组件: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
我们想要爷爷和儿子进行传值
- props可以,但是需要一级一级的传递
- 事件总线可以,但是当多人合作的时候不方便维护
- Vuex可以,但是当传递的数据量小的时候,又不划算。
- $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