vue通信应该是一个老生常谈的问题,面试也经常会有相关的问题,我之前作答往往是prop父传子,emit子传父,vuex复杂组件之间通信,bus全局通信如果不采用vue的通信方式我们还可以使用web存储(不推荐),自我感觉已经算很完美的回答了呀,现在想想可真的太无知了,今天详细介绍一下vue各种通信方式
1.prop、emit、vuex
上面几种通信都是最基础的建议移步vue官网查阅
2.provide和inject
provide和inject偏冷门的一个api可以跨层级通信,向下传递也就是说可用于给后辈传递信息
app
provide: { title: "我是你爷爷组件" },
chid或者grandChild
inject: ["title"]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>provide-inject</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
let Child = {
template: `<div>
<span>子组建</span> 接收数据{{title}}
</div>`,
// 可接收到Grandfather组件数据
inject: ['title']
}
let Father = {
template: `<div>{{name}}<Child/>{{title}}</div>`,
data() {
return {
name: '父组件'
}
},
// 可接收到Grandfather组件数据
inject: ['title'],
components: {
Child
}
}
let Grandfather = {
template: '<div><Father/></div>',
provide: {
title: '我是祖父组件'
},
components: {
Father
}
}
new Vue({
el: '#app',
template: `<div><Grandfather/>{{title}}</div>`,
components: {
Grandfather
}
})
</script>
</body>
</html>
3.dispatch
根据获取$parent获取父级元素来通信
main.js
// dispatch从小往上传递数据
Vue.prototype.$dispatch = function (eventName, data) {
let parent = this.$parent
while (parent) {
parent.$emit(eventName, data)
parent = parent.$parent
}
}
grandChild
<button @click="dispatch">通知父组件</button>
methods: {
dispatch() {
this.$dispatch("dispatch", "来自孙子");
}
}
app或child
mounted() {
this.$on("dispatch", grandChild => {
this.grandChild = grandChild;
});
},
4.boardcast
根据获取$chidren向下查找传递数据
main.js
Vue.prototype.$boardcast = function (eventName, data) {
boardcast.call(this, eventName, data)
}
function boardcast(eventName, data) {
this.$children.forEach(child => {
child.$emit(eventName, data)
if (child.$children.length) {
boardcast.call(child, eventName, data)
}
});
}
grandChild或者child
<div style="color: yellow">boardcast{{msg}}</div>
mounted() {
this.$on("boardcast", msg => {
this.msg = msg;
});
},
5.attrs和listeners
// 一级组件Home
<template>
<div class="home">
<h5>我是一级组件</h5>
<img alt="Vue logo" src="../assets/logo.png" />
<HelloWorld
msg="Welcome to Your Vue.js App"
:message="message"
:messageChild="messageChild"
@getTwoData="getTwoData"
@getThreeData="getThreeData"
/>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from "@/components/HelloWorld.vue";
export default {
name: "home",
components: {
HelloWorld
},
data() {
return {
message: "传给二级组件",
messageChild: "传给三级组件"
};
},
methods: {
getTwoData(val) {
console.log("二级组件");
},
getThreeData(val) {
console.log("三级组件");
}
}
};
</script>
// 二级组件 HelloWorld
<template>
<div class="hello">
<h5>我是二级组件</h5>
<input type="text" v-model="helloVal" @input="listenData(helloVal)" />
<!-- 三级组件中能直接触发getThreeData的原因在于 二级组件调用三级组件时 使用 v-on 绑定了$listeners 属性 -->
<!-- 通过v-bind 绑定$attrs属性,三级组件可以直接获取到Home组件中传递下来的props(除了二级组件中props声明的) -->
*<hello-child v-bind="$attrs" v-on="$listeners"></hello-child>*
</div>
</template>
<script>
import HelloChild from "./HelloChild";
export default {
name: "HelloWorld",
data() {
return {
helloVal: this.message
};
},
props: {
msg: String,
message: String // Home传递过来的数据
},
components: {
"hello-child": HelloChild
},
methods: {
listenData(val) {
this.$emit("getTwoData", val);
}
}
};
</script>
// 三级组件HelloChild
<template>
<div class="hello-child">
<P>我是三级子组件</P>
<input type="text" v-model="childVal" @input="listenChildVal(childVal)" />
<input type="text" v-model="msg" />
<!--无法获取HelloWorld组件中props声明后的message数据-->
</div>
</template>
<script>
export default {
props: {
messageChild: String,
message: String // 无法获取HelloWorld组件中props声明后的message数据
},
data() {
return {
msg: this.message,
childVal: this.messageChild
};
},
methods: {
listenChildVal(val) {
this.$emit("getThreeData", val);
}
}
};
</script>
<style lang="less">
</style>
6.$parent
$children
在子组件中通过this.$parent获取父组件的方法和data数据,
在父组件中通过this.$children获取的是所有子组件
Vue.component("Child", {
template: '<div>
<div>{{childMsg}}</div>
<button @click="changePrentData">改变父组件数据</button>
</div>',
data() {
return {
childMsg: '你好,我是子组件!'
}
},
method: {
changePrentData() {
this.$parent.msg = '更改父组件'
}
}
})
Vue.component("Parent", {
template: '
<div>
<child/>
<button @click="changePrentData">改变子组件件数据</button>
</div>',
data() {
msg: "你好!"
},
method: {
changePrentData() {
this.$children[0].childMsg= '更改子组件'
}
}
})
7.bus(总线)通信
兄弟,父子,外甥都可以使用bus进行通信
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>bus兄弟组件通信</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak></div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
// (bus)总线定义
let bus = new Vue();
let ComponentA = {
template: `<div>
<button @click="sendB">给b组建传消息</button>
</div>`,
methods: {
sendB() {
bus.$emit('getA', '你好B,我是A')
}
}
}
// 接收a的消息
let ChildB = {
template: `<div> {{getaMsg}}</div>`,
data() {
return {
getaMsg: 'dssds'
}
},
created() {
bus.$on('getA', (val) => {
this.getaMsg = val
console.log(this)
})
},
}
// 接收a的消息
let ComponentB = {
template: `<div>
<span>{{valueA}}</span>
<ChildB/>
</div>`,
data() {
return {
valueA: '1111'
}
},
created() {
// bus.$on('getA', (val) => {
// this.valueA = val
// console.log(this)
// })
let that = this
bus.$on('getA', function(val) {
that.valueA = val
})
},
components: {
ChildB
}
}
// 接收a的消息
new Vue({
el: '#app',
template: `<div>
<ComponentA/>
<ComponentB/>
{{rootVal}}
</div>`,
data() {
return {
rootVal: 'dsdsds'
}
},
created() {
let that = this
bus.$on('getA', (val) => {
this.rootVal = val
})
},
components: {
ComponentB,
ComponentA
}
})
</script>
</body>
</html>
详细操作
https://github.com/cshuanglu/vue-communication.git