目录
简介
Vue组件通信是指不同组件之间进行数据传递、事件触发等交互操作的过程。
在Vue中,有以下几种常用的组件通信方式:
- 父子组件通信
- 兄弟组件通信
- 跨级组件通信
- 全局组件通信
- $attrs / $listeners
- $parent / children / ref
- v-model / .sync
示例
父子组件通信
父子组件通信: 父组件通过props将数据传递给子组件,子组件通过emit触发事件向父组件传递数据。
父组件:
<template>
<div>
<child-component :message="message" @update-message="updateMessage"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
data() {
return {
message: 'Hello Vue!'
};
},
methods: {
updateMessage(newMessage) {
this.message = newMessage;
}
},
components: {
ChildComponent
}
};
</script>
子组件:
<template>
<div>
<p>{{ message }}</p>
<button @click="sendMessage">Send Message</button>
</div>
</template>
<script>
export default {
props: ['message'],
methods: {
sendMessage() {
this.$emit('update-message', 'New Message');
}
}
};
</script>
兄弟组件通信
兄弟组件通信:通过共享状态管理工具(如Vuex)或者事件总线方式实现兄弟组件之间的通信。
共享状态管理工具示例(使用Vuex):
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
message: 'Hello Vue!'
},
mutations: {
updateMessage(state, newMessage) {
state.message = newMessage;
}
}
});
组件示例:
<template>
<div>
<p>{{ message }}</p>
<button @click="sendMessage">Send Message</button>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
computed: {
...mapState(['message'])
},
methods: {
...mapMutations(['updateMessage']),
sendMessage() {
this.updateMessage('New Message');
}
}
};
</script>
跨级组件通信
跨级组件通信:通过provide/inject方式实现跨级组件之间的通信。
祖先组件示例:
<template>
<div>
<child-component></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
provide() {
return {
message: 'Hello Vue!'
}
},
components: {
ChildComponent
}
};
</script>
子组件示例:
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
inject: ['message']
};
</script>
全局组件通信
全局组件通信:通过Vue实例的$emit/$on方法来触发和监听全局事件。
示例:
// main.js
import Vue from 'vue';
export const bus = new Vue();
// Component A
import { bus } from './main.js';
export default {
methods: {
sendMessage() {
bus.$emit('update-message', 'New Message');
}
}
};
// Component B
import { bus } from './main.js';
export default {
mounted() {
bus.$on('update-message', (newMessage) => {
this.message = newMessage;
});
}
};
$attrs / $listeners
$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
父组件:
<template>
<div>
<h2>祖先组件</h2>
<child-com1
:f="f"
:b="b"
:c="coo"
:d="d"
title="data"
></child-com1>
</div>
</template>
<script>
import "./childCom1.vue";
export default {
components: { childCom1 },
data() {
return {
f: "Javascript",
b: "Html",
c: "CSS",
d: "Vue"
};
}
};
</script>
儿子组件:
<template class="border">
<div>
<p>f: {{ f }}</p>
<p>childCom1的$attrs: {{ $attrs }}</p>
<child-com2 v-bind="$attrs"></child-com2>
</div>
</template>
<script>
import "./childCom2.vue";
export default {
components: {
childCom2
},
inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
props: {
f: String // f作为props属性绑定
},
created() {
console.log(this.$attrs); // { "b": "Html", "c": "CSS", "d": "Vue", "title": "data" }
}
};
</script>
孙子组件
<template>
<div class="border">
<p>b: {{ b }}</p>
<p>childCom2: {{ $attrs }}</p>
<child-com3 v-bind="$attrs"></child-com3>
</div>
</template>
<script>
import "./childCom3.vue";
export default {
components: {
childCom3
},
inheritAttrs: false,
props: {
b: String
},
created() {
console.log(this.$attrs); // { "c": "CSS", "d": "Vue", "title": "data" }
}
};
</script>
$parent / children / ref
$parent / children / ref: 这几种方式都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据,这里采用ref示例。
父组件
<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.title); // Vue.js
comA.sayHello(); // 弹窗
}
}
</script>
子组件
export default {
data () {
return {
title: 'Vue.js'
}
},
methods: {
sayHello () {
window.alert('Hello');
}
}
}
v-model / .sync
v-model是 Vue.js 提供的一个语法糖,用于简化表单元素的双向绑定,它在内部实际上是使用了 :value
和 @input
这两个指令的结合形式。
v-model缺点:如需自定义传值,子组件内书写$emit('input', data) 因为名称固定为input所以单个组件只能使用一次。
.sync避免了v-model的这个问题可以自定义命名。
vue3取消了.sync这种语法,使用v-model:title 语法代替
v-model父组件:
<template>
<div id="app">
<img src="./assets/logo.png">
<!--<router-view/>-->
<car v-model="index"></car>
<div>{{index}}</div>
</div>
</template>
<script>
import Car from "./car.vue"
export default {
data(){
return{
index:0
}
},
name: 'App',
components:{Car}
}
</script>
v-model子组件:
<template>
<div @click="$emit('input',value+1)">汽车</div>
</template>
<script>
export default{
props:["value"]
}
</script>
.sync父组件:
<template>
<div>
<h1>父组件:{{childSubTitle}}</h1>
<Child :subTitle.sync="childSubTitle"></Child>
</div>
</template>
<script>
import Child from './child.vue'
export default {
data() {
return {
childSubTitle:'数据',
}
},
components:{
Child
},
}
</script>
.sync子组件:
<template>
<div class="child">
<h4>{{subTitle}}</h4>
<button @click="changeSubTitle">改变</button>
</div>
</template>
<script>
export default {
data() {
return {
newSubTitle:'测试数据'
};
},
props: {
subTitle:{
type:String,
default:'',
}
},
methods:{
changeSubTitle(){
this.$emit('update:subTitle',this.newSubTitle)
},
},
};
</script>