1.组件通信的分类
1.父子组件通信(props、$emit、ref、v-model、具名插槽、作用域插槽)
2.兄弟组件通信($bus、$parent、消息订阅与发布)
3.祖孙与后代之间的通信($attrs、$listener、provide、inject)
4.非关系组件之间的通信(vuex)
2.组件通信方法
2.1 Vue2组件通信方式
2.1.1 通过props传值
//Child.vue
<template>
<div class="about">
<p>{{ name }}</p>
<p>{{ age }}</p>
</div>
</template>
<script>
export default {
// 第一种写法
// props: ['name', 'age']
props: {
name: {
type: String,
default: '默认值'
},
age: {
type: Number,
default: 18
}
}
}
</script>
//Farther.vue
<template>
<div class="home">
<ChildView name="aDong" age="21"></ChildView>
</div>
</template>
<script>
import ChildView from './ChildView.vue'
export default {
name: 'FatherView',
components: {
ChildView
}
}
</script>
2.1.2 使用v-model传值
在vue
中,v-model
的值相当于默认
传递了一个名为 value 的 prop
和一个名为 input 的方法
(注意,这个value
的prop
是需要在自定义组件内声明的)
//Father.vue
<template>
<div >
<ChildView v-model="value"></ChildView>
{{ value }}
</div>
</template>
<script>
import ChildView from './ChildView.vue'
export default {
name: 'FatherView',
components: {
ChildView
},
data () {
return {
value: '我是父组件aDong'
}
}
}
</script>
//Child.vue
<template>
<div class="about">
<input type="text" :value="obj" @input="handlerChange">
</div>
</template>
<script>
export default {
// 这里的值一定要与上面:value后面绑定的名称一致
props: ['obj'],
methods: {
handlerChange (e) {
this.$emit('input', e.target.value)
}
}
}
</script>
2.1.3 使用$emit传值
//Father.vue
<template>
<div >
<ChildView @add="changeNum($event)"></ChildView>
{{ num }}
</div>
</template>
<script>
import ChildView from './ChildView.vue'
export default {
name: 'FatherView',
components: {
ChildView
},
data () {
return {
num: 21
}
},
methods: {
changeNum () {
this.num++
}
}
}
</script>
//Child.vue
<template>
<div class="about">
<button @click="handler">点击我年龄+1</button>
</div>
</template>
<script>
export default {
methods: {
handler () {
this.$emit('add')
}
}
}
</script>
2.1.4 使用ref传值
//Father.vue
<template>
<div >
<ChildView ref="child"></ChildView>
<button @click="getAge">点击我获取子组件数据</button>
</div>
</template>
<script>
import ChildView from './ChildView.vue'
export default {
name: 'FatherView',
components: {
ChildView
},
methods: {
getAge () {
console.log('父组件拿到了子组件数据', this.$refs.child.age)// 父组件拿到了子组件数据 21
}
}
}
</script>
//Child.vue
<template>
<div class="about">
</div>
</template>
<script>
export default {
data () {
return {
age: 21
}
}
}
</script>
2.1.5 全局事件总线EventBus
主要应用于兄弟组件
// 定义一个全局事件总线
export class Bus {
constructor () {
this.callbacks = {} //
}
$on (name, fn) {
this.callbacks[name] = this.callbacks[name] || []
this.callbacks[name].push(fn)
}
$emit (name, args) {
if (this.callbacks[name]) {
this.callbacks[name].forEach((cb) => cb(args))
}
}
}
//一定要在main.js文件中将bus挂载到vue实例原型身上
Vue.prototype.$bus = new Bus()
//child1.vue
this.$bus.$emit('foo')//触发事件
//child2.vue
this.$bus.$on('foo',this.handler)//绑定事件
2.1.6 parent或者root
适用于兄弟组件之间通信
//child1.vue
this.$parent.on('add',this.add)
//child2.vue
this.$parent.emit('add')
2.1.7 attrs与listener
多层嵌套组件传递数据时,如果只是传递数据,而不做中间处理的话就可以用这个,比如父组件向孙子组件传递数据时
$attrs
:包含父作用域里除 class 和 style 除外的非 props 属性集合。通过 this.$attrs 获取父作用域中所有符合条件的属性集合,然后还要继续传给子组件内部的其他组件,就可以通过 v-bind="$attrs"
$listeners
:包含父作用域里 .native 除外的监听事件集合。如果还要继续传给子组件内部的其他组件,就可以通过 v-on="$linteners"
使用方式是相同的
// 父组件
<template>
<div class="">
<HelloWorld :name="name" val="你好s-r-d"></HelloWorld>
</div>
</template>
<script>
import HelloWorld from "../components/HelloWorld.vue";
export default {
data() {
return {
name: "父组件数据",
};
},
components: { HelloWorld },
};
</script>
// 子组件
<template>
<div>
<input type="text" />
// 孙组件
<SunChild v-bind="$attrs"></SunChild>
</div>
</template>
<script>
import SunChild from "./sun-child.vue";
export default {
data() {
return {};
},
mounted() {
console.log(this.$attrs); // 可以拿到父组件传来的数据
},
components: { SunChild },
};
</script>
// 孙组件
export default {
mounted() {
console.log(this.$attrs);
},
};
listener的使用方式
// 给Grandson隔代传值
<Child2 msg="lalala" @some-event="onSomeEvent"></Child2>
// Child2 做展开
<Grandson v-bind="$attrs" v-on="$listeners"></Grandson>
// Grandson使用
<div @click="$emit('some-event', 'msg from grandson')">
{{msg}}
</div>
2.1.8 provide和inject
用于祖先给后代组件传值
provide(){
return {
foo:'foo'
}
}
inject:['foo']
2.1.9 Vuex
// vuex 里定义共享的数据,在哪个组件都可以访问
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import vuexPersist from "vuex-persist";
export default new Vuex.Store({
state: {
data: 'vuex 的数据',
a: 1,
b: 2,
},
mutations: {},
actions: {},
modules: {},
getters: {
num(state) {
return state.a + state.b
}
},
})
2.1.10 具名插槽和作用域插槽
具名插槽是在父组件中通过slot属性,给插槽命名,在子组件中通过slot标签,根据定义好的名字填充到对应的位置。
作用域插槽是带数据的插槽,子组件提供给父组件的参数,父组件根据子组件传过来的插槽数据来进行不同的展现和填充内容。在标签中通过 v-slot="value" 来接受数据。
// 父组件
<template>
<div class="">
<HelloWorld>
<template v-slot:a="value">
<h2>{{ name }}</h2> // 这个是具名插槽
<!-- value 是子组件传过来的数据 -->
<h2>{{ value.value }}</h2> // 这个是作用域插槽
</template>
</HelloWorld>
</div>
</template>
<script>
import HelloWorld from "../components/HelloWorld.vue";
export default {
data() {
return {
name: "具名插槽 slot插槽传数据 父传子",
};
},
components: { HelloWorld },
};
</script>
// 子组件
<template>
<div>
<slot name="a" :value="value"></slot>
</div>
</template>
<script>
export default {
data() {
return {
value: "我是子组件数据 作用域插槽子传父 srd",
};
},
methods: {},
};
</script>
2.1.11 $root
$root
可以拿到 App.vue 里的数据和方法
// HelloWorld 子组件添加数据
<template>
<div></div>
</template>
<script>
export default {
data() {
return {};
},
mounted() {
// 写入根组件的数据
this.$root.foo = 2;
},
methods: {},
};
</script>
// Home 父组件访问数据
<template>
<div class="">
<HelloWorld></HelloWorld>
</div>
</template>
<script>
import HelloWorld from "../components/HelloWorld.vue";
export default {
data() {
return {};
},
mounted() {
// 获取根组件的数据
console.log(this.$root.foo);
},
components: { HelloWorld }
};
</script>