组件通信
1、props 和$emit 通信
<!-- index -->
<template>
<div>
<h2>组件通信</h2>
<Child1 msg="props 通信" @custom-event="OnCustomEvent"></Child1>
</div>
</template>
<script>
import Child1 from '@/components/communication/Child1.vue';
export default {
components: {
Child1
},
methods:{
OnCustomEvent(msg){
console.log(msg);
}
}
};
</script>
<!-- Child1 -->
<template>
<div @click="$emit('custom-event', '自定义事件 - Child1')">
<h3>Child1 组件</h3>
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default: ''
}
}
};
</script>
如上例中,需注意
- Child1组件中$emit 暴露出来的方法名 => custom-event 与 Parent组件中@XXX(XXX要与Child1组件的方法名一致)
- Child1组建中props所传的值,不建议修改,一般都会将数据缓存在data里面。
2、 $parent 和 $children
<!-- index -->
<template>
<div>
<h2>组件通信</h2>
<Child1 msg="props 通信" @custom-event="OnCustomEvent"></Child1>
<Child2></Child2>
</div>
</template>
<script>
import Child1 from '@/components/communication/Child1.vue';
import Child2 from '@/components/communication/Child2.vue';
export default {
components: {
Child1,
Child2
},
methods:{
OnCustomEvent(msg){
console.log(msg);
}
},
mounted(){
// 需要注意 $children 并不保证顺序,也不是响应式的
this.$children[1].sendToChild1();
}
};
</script>
<!-- Child2 -->
<template>
<div>
<h3>
Child2 组件
</h3>
<button @click="sendToChild1">给Child1发送消息</button>
</div>
</template>
<script>
export default {
methods: {
sendToChild1() {
this.$parent.$emit('event-from-child2', 'some msg from child2');
}
}
};
</script>
<!-- Child1 -->
<template>
<div @click="$emit('custom-event', '自定义事件 - Child1')">
<h3>Child1 组件</h3>
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default: ""
}
},
mounted() {
this.$parent.$on("event-from-child2", msg => {
console.log(msg);
});
}
};
</script>
如上例,需注意$children 从0开始,并且不保证顺序,也不是响应式的。比如 有些组件使用v-if,或者异步组件,这样就会导致顺序错乱
3、$bus
// main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
Vue.prototype.$bus = new Vue()
new Vue({
render: h => h(App),
}).$mount('#app')
<!-- Child2 -->
<template>
<div>
<h3>
Child2 组件
</h3>
<button @click="sendToChild1">给Child1发送消息</button>
</div>
</template>
<script>
export default {
methods: {
sendToChild1() {
this.$bus.$emit('event-from-child2', 'some msg from child2');
}
}
};
</script>
<!-- Child1 -->
<template>
<div @click="$emit('custom-event', '自定义事件 - Child1')">
<h3>Child1 组件</h3>
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default: ""
}
},
mounted() {
this.$bus.$on("event-from-child2", msg => {
console.log(msg);
});
}
};
</script>
如上例,可以发现$bus与$parent 的通信方式有共同性,就是使用了同一个“中间人”进行通信。但发现$parent只适用于子父之间的通信,而$bus是任意关系都可以通信
4、$refs
<!-- index -->
<template>
<div>
<h2>组件通信</h2>
<Child1 msg="props 通信" @custom-event="OnCustomEvent"></Child1>
<Child2 ref="child2"></Child2>
</div>
</template>
<script>
import Child1 from '@/components/communication/Child1.vue';
import Child2 from '@/components/communication/Child2.vue';
export default {
components: {
Child1,
Child2
},
methods:{
OnCustomEvent(msg){
console.log(msg);
}
},
mounted(){
// 需要注意 $children 并不保证顺序,也不是响应式的
// this.$children[1].sendToChild1();
// $refs
this.$refs.child2.sendToChild1();
}
};
</script>
如上例,可以使用ref指定组件名称,类似于id,名称不可重复,然后使用$refs.refName可以访问组件实例
5、$attrs和$listeners
<!-- index -->
<template>
<div>
<h2>组件通信</h2>
<Child1 msg="props 通信" @custom-event="OnCustomEvent"></Child1>
<Child2 ref="child2" msg="some message"></Child2>
<Parent msg="来自Parent 组件的消息" @send-to-grandpa="onFoo"></Parent>
</div>
</template>
<script>
import Child1 from '@/components/communication/Child1';
import Child2 from '@/components/communication/Child2';
import Parent from '@/components/communication/Parent';
export default {
components: {
Child1,
Child2,
Parent
},
methods: {
OnCustomEvent(param) {
console.log(param);
},
onFoo(msg) {
console.log(msg);
}
},
mounted() {
// 需要注意 $children 并不保证顺序,也不是响应式的
// this.$children[1].sendToChild1();
// refs
this.$refs.child2.sendToChild1();
}
};
</script>
<!-- Parent -->
<template>
<div>
<h3>
Parent 组件
</h3>
<Child2 v-bind="$attrs" v-on="$listeners"></Child2>
</div>
</template>
<script>
import Child2 from '@/components/communication/Child2.vue';
export default {
components: {
Child2
}
};
</script>
<!-- Child2 -->
<template>
<div>
<h3>child2</h3>
<!-- $attrs -->
<p>{{ $attrs.msg }}</p>
<button @click="sendToChild1">给child1发送消息</button>
<button @click="sendToGrandpa">给grandpa发送消息</button>
</div>
</template>
<script>
export default {
methods: {
sendToChild1() {
// 利用事件总线发送事件
// this.$bus.$emit('event-from-child2', 'some msg from child2')
this.$parent.$emit('event-from-child2', 'some msg from child2');
},
sendToGrandpa() {
this.$emit('send-to-grandpa', '爷爷,我来看你啦');
}
}
};
</script>
$attrs: 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
$listeners: 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
6、inject和provide
<!-- index -->
<template>
<div>
<h2>组件通信</h2>
<Child1 msg="props 通信" @custom-event="OnCustomEvent"></Child1>
<Child2 ref="child2" msg="some message"></Child2>
<Parent msg="来自Parent 组件的消息" @send-to-grandpa="onFoo"></Parent>
</div>
</template>
<script>
import Child1 from '@/components/communication/Child1';
import Child2 from '@/components/communication/Child2';
import Parent from '@/components/communication/Parent';
export default {
provide() {
return {
bar: 'bar'
};
},
components: {
Child1,
Child2,
Parent
},
methods: {
OnCustomEvent(param) {
console.log(param);
},
onFoo(msg) {
console.log(msg);
}
},
mounted() {
// 需要注意 $children 并不保证顺序,也不是响应式的
// this.$children[1].sendToChild1();
// refs
this.$refs.child2.sendToChild1();
}
};
</script>
<!-- Child2 -->
<template>
<div>
<h3>
Child2 组件
</h3>
<p>attrs : {{ $attrs.msg }}</p>
<!-- inject/provide -->
<!-- <p>{{ bar }}</p> -->
<p>{{ bar1 }}</p>
<button @click="sendToChild1">给Child1发送消息</button>
<button @click="sendToGrandpa">给grandpa发送消息</button>
</div>
</template>
<script>
export default {
// inject: ['bar'], // 类似于props
inject: {
bar1: {
from: 'bar',
default: 'barrrrrr'
}
},
methods: {
sendToChild1() {
// 事件总线发送事件 bus
this.$bus.$emit('event-from-child2', 'some msg from child2');
// parent
// this.$parent.$emit('event-from-child2', 'some msg from child2');
// this.$emit('foo', '爷孙通信,父亲转发');
},
sendToGrandpa() {
this.$emit('send-to-grandpa', '爷爷,我来看你啦');
}
}
};
</script>
详情可看官方文档 provide/inject
7、Vuex
创建唯一的全局数据管理者store,通过它管理数据并通知组件状态变更
插槽
插槽语法是Vue实现内容分发API,用于复合组件开发。该技术在通用组件库开发中有大量应用。
<!-- index -->
<template>
<div>
<h2>插槽</h2>
<Layout>
<!-- 具名插槽 -->
<template v-slot:header>好好学习,追上距离</template>
<!-- 匿名插槽 -->
<template>content...</template>
<!-- 作用域插槽 -->
<template v-slot:footer="{ fc }">{{ fc }}</template>
</Layout>
</div>
</template>
<script>
import Layout from '@/components/slots/Layout.vue';
export default {
components: {
Layout
}
};
</script>
<!-- Layout -->
<template>
<div>
<div class="header">
<slot name="header"></slot>
</div>
<div class="body">
<slot></slot>
</div>
<div class="footer">
<slot name="footer" :fc="footerContent"></slot>
</div>
</div>
</template>
<script>
export default {
data() {
return {
remark: [
'好好学习,天天向上',
'学习永远不晚',
'学习知识要善于思考,思考,再思考',
'学习的敌人是自己的满足,要认真学习一点东西,必须从不自满开始',
'构成我们学习最大障碍的是已知的东西,而不是未知的东西',
'在今天和明天之间,有一段很长的时间;趁你还有精神的时候,学习迅速办事',
'三人行必有我师焉;择其善者而从之,其不善者而改之'
]
}
},
computed: {
footerContent() {
return this.remark[new Date().getDay() - 1];
}
}
}
</script>