vue 组件间通信(父子组件间传参,兄弟组件传参)
前言 :vue 项目中简单可以分为【父向子传参】、【子向父传参】、【组件间传参】,整理后已分享在下面(代码可直接复制粘贴使用)
1.父向子传参
通过props从父向子组件传递函数,调用函数改变父组件数据
老规矩先上图
代码:
- 父组件
<template>
<div class="home" style="background:red">
<h1>父页面</h1>
<!-- 前面(parentData)自定义名称便于子组件调用,后面(userName)要传递数据名 -->
<childA :parentData="userName"/>
</div>
</template>
<script>
// @ is an alias to /src
import childA from '@/components/childA.vue'
export default {
name: 'Home',
data() {
return {
userName:["大明","二明","小明"]
};
},
components: {
childA
}
}
</script>
- 子组件
<template>
<div class="hello" style="background:pink" v-cloak>
子页面
<h1>{{parentData }}</h1>
<!-- 遍历传递过来的值,然后呈现到页面 -->
<p v-for="(item, index) in parentData" :key="index">{{item }} </p>
</div>
</template>
<script>
export default {
name: 'childA',
//子组件通过props来接收数据:
//方式1:
props: ['parentData'] //这个相对用了较多
//方式2 :
// props: {
// parentData: Array //指定传入的类型
// }
//方式3:
// props: {
// parentData: {
// type: Array, //指定传入的类型 也可以是一个自定义构造器函数,使用 instanceof 检测。
// default: () => [] //可以设置默认的值 === function () { return [] }
// }
// }
}
</script>
<style>
[v-cloak] {
display: none;
}
</style>
组件中的数据共有三种形式:data、props、computed
注意 props 会在组件实例创建之前进行校验,所以在 default 或 validator 函数里,诸如 data、computed 或 methods 等实例属性还无法使用;
提示:
- 原因:异步请求时,数据还没有获取到但是此时已经渲染节点了
- 解决方案:可以在父组件需要传递数据的节点加上 v-if=isReady(isReady默认为false),异步请求获 取数据后(isReady赋值为true),v-if = isReady
- 【这里加上了 v-cloak 尽量优化代码】
2.子向父传参
2.1 子组件通过自定义事件$emit方法(用来触发事件,详情见官网)传递参数:
先上图
代码:
- 父组件
<template>
<div class="home" style="background:red">
<h1>父页面</h1>
<!-- childEvent:是子组件中定义的方法;
parentEvent:触发childEvent会调用父组件的方法 -->
<childA @childEvent="parentEvent"/>
<p>{{msg}}</p>
</div>
</template>
<script>
import childA from '@/components/childA'
export default {
data() {
return {
msg:'123'
};
},
name: 'Home',
users: '123',
components: { childA },
methods:{
parentEvent(data) {//data就是子组件传过来的值
this.msg = data //将子组件传过来的值赋值给父页面展示
},
}
}
</script>
- 子组件
<template>
<div class="hello" style="background:pink" v-cloak>
子页面
<!-- 绑定一个点击事件 -->
<button @click="sendData">点击给父组件传值</button>
</div>
</template>
<script>
export default {
name: 'childA',
data() {
return {
msg:"子向父传参"
}
},
methods:{
sendData() {
//自定义事件 传递值“子向父组件传值-----嘿嘿嘿”
this.$emit("childEvent","子向父组件传值-----嘿嘿嘿");
},
}
}
</script>
<style>
[v-cloak] {
display: none;
}
</style>
小结:
子组件通过事件和父组件通信,实际上就是子组件把自己的数据传到父组件。
2.2 通过ref(详情见官网)属性在父组件中直接取得子组件的数据(data)
在有些开发场景下,需要从子面直接拿到数据渲染到父页面,但是子组件里没有类似“按钮”的东西,因而无法制造原生事件,同时也没办法找到一个触发自定义事件的时机的时候,这时候再用父组件调用子组件方法就很别扭,可在父组件中为子组件设置ref的话, 就可以直接通过vm.$refs.[子组件的ref].[子组件的属性]去拿到数据
代码:
- 父组件
<template>
<div class="home" style="background:red">
<h1>父页面:{{msg || '暂无数据'}}</h1>
<!-- 增加一个触发事件 方便理解 -->
<button @click="getChildData">接受数据</button>
<childA ref="childA"/>
</div>
</template>
<script>
import childA from '@/components/childA'
export default {
data() {
return {
msg:''
};
},
components: { childA },
// created(){ 这里不能再created赋值 具体请看vue生命周期
// this.msg = this.$refs.childA.data
// },
//可直接调用赋值即可
mounted(){
this.getChildData()
},
methods:{
getChildData(){
this.msg = this.$refs.childA.data
}
}
}
</script>
- 子组件
<template>
<div class="hello" style="background: pink" v-cloak>
子页面
<p>我是子组件,我所拥有的数据: {{ data }}</p>
</div>
</template>
<script>
export default {
name: "childA",
data() {
return {
data: [
{
age: 18,
sex: "女",
},
],
};
},
};
</script>
<style>
</style>
小结:
个人觉得这个也算是一种简单的方法,有场景需求也可以用上,也不算太难理解。
2.3 通过sync(详情见官网)实现数据双向绑定, 从而同步父子组件数据
在某些情况下子组件向父组件传递数据时,父子组件中的数据要是每时每刻都同步
- 父组件
<template>
<div id="father">
<div>
父组件
<p>数量(1): {{ num1 }}</p>
<p>数量(2): {{ num2 }}</p>
<childA :num1.sync="num1" :num2.sync="num2" >
</childA>
</div>
</div>
</template>
<script>
import childA from '@/components/childA'
export default {
data() {
return {
num1: 0,
num2: 1,
};
},
components: { childA },
}
</script>
- 子组件
<template>
<div style="background:pink">
<p>我是子组件</p>
<p>数量(1): {{ num1 }}</p>
<p>数量(2):{{ num2 }}</p>
<button @click="change('num1')">加数量1</button>
<button @click="change('num2')">加数量2</button>
</div>
</template>
<script>
export default {
//这里用props声明接收num1,num2并使用,后面是自己定义的数据类型 根据所需的数据类型自定义(最好数据序列化处理)
props: {
num1: Number,
num2: Number,
},
methods: {
change(data) {
let newData = this[data] + 1;
//当子组件中数据改变的时,这里把 newData传给父组件
this.$emit(`update:${data}`, newData)
//this.$emit('update', newData)
}
}
}
</script>
<style scoped>
</style>
补充:
当sync修饰的prop是个对象
父组件:
<template>
<div id="father">
<div>
父组件
<p>数量(1): {{ stock.num1 }}</p>
<p>数量(2): {{ stock.num2 }}</p>
<childA :stock.sync="stock">
</childA>
</div>
</div>
</template>
<script>
import childA from '@/components/childA'
export default {
data() {
return {
stock: {
num1: 0,
num2: 1,
}
};
},
components: { childA },
}
</script>
子组件:
<template>
<div style="background:pink">
<p>我是子组件</p>
<p>数量(1): {{ stock.num1 }}</p>
<p>数量(2):{{ stock.num2 }}</p>
<button @click="change('num1')">加数量1</button>
<button @click="change('num2')">加数量2</button>
</div>
</template>
<script>
export default {
//这里用props声明接收nObject并使用
props: {
stock:Object
},
methods: {
change(data) {
let newData = JSON.parse(JSON.stringify(this.stock))
newData[data] += 1
//当子组件中数据改变的时,这里把 newData传给父组件
this.$emit(`update:stock`, newData)
}
}
}
</script>
<style scoped>
</style>
小结:
- 这种和 2.1 通过自定义事件(emit)从子组件向父组件中传递数据有区别,两者有着父子组件关系上的不同;
- 这种传参父可以改变子(数据), 但子不能直接改变父(数据), 父中数据的变动只能由它自己决定;
- 不要通过在子组件中修改引用类型props达到“父子组件数据同步”,会使数据流变得更加难以分析
还有几种可以也可以用来子组件向父组件传参通信,这里放在组件间之间的传参一起说了
3.组件之间传参通信
3.1通过eventBus(即通过on监听、emit触发的方式) 项目小页面少用eventBus
老规矩先上图
代码:
父组件:
<template>
<div id="father">
<div>
父组件
<childA ></childA>
<childB ></childB>
</div>
</div>
</template>
<script>
import childA from '@/components/childA'
import childB from '@/components/childB'
export default {
data() {
return {
};
},
components: { childA,childB},
}
</script>
1. eventBus.js定义一个新的vue实例专门用于传递数据,并导出
import Vue from 'vue';
export default new Vue();
2.childA 定义传递的方法名和传输内容,点击事件或钩子函数触发eventBus.emit事件
<template>
<div class="childA">
<p>我是子组件A</p>
<!-- 组件A通过时间向组件B传参 -->
<button @click="sendToB">点击传参B</button>
</div>
</template>
<script>
import eventBus from "@/utils/eventBus";
export default {
data() {
return {
msg:"组件A的数据过来了"
};
},
methods: {
sendToB(){
eventBus.$emit("eventDataA",this.msg);
}
}
}
</script>
<style scoped>
.childA {
margin: 0 auto;
width: 400px;
height: 400px;
border: 1px solid #000;
}
</style>
3. childB接收传递过来的数据
<template>
<div class="childB">
<p>我是子组件B</p>
<p>组件A传过来的参数为: {{msg}}</p>
<button @click="sendToA">点击传参A</button>
</div>
</template>
<script>
import eventBus from "@/utils/eventBus";
export default {
data() {
return {
msg:""
};
},
mounted(){
this.getChildAData()
},
methods: {
getChildAData(){
let that = this //这个this是vue的实例 重新声明一下 好区分evevntBus中的vue实例
eventBus.$on('eventDataA', (e) => {
that.msg = e
console.log(e);
})
},
sendToA(){
}
}
}
</script>
<style scoped>
.childB{
margin: 0 auto;
width: 400px;
height: 400px;
border:5px solid #000;
}
</style>
小结:
可以总结为3步:
- 定义一个新的vue实例专门用于传递数据,并导出;
- 定义传递的方法名和传输内容,点击事件或钩子函数触发eventBus.emit事件
- 接收传递过来的数据
注意:
1. enentBus是一个另一个新的Vue实例,区分两个this所代表得vue实例;
2. 在开发时候要记得解绑enentBus;
created () {
// 解绑enentBus
EventBus.$off('eventDataA')
}
3.1通过vuex进行传值
-
先要下载vuex,再src中建立store文件夹建立一个index.js文件 (如果在3.0脚手架建立的项目,在配置项中配置过则不需要)
代码:store--->index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
name:"navy_c"
},
mutations: {
getData(a,b) {
this.state.name = b
}
},
actions: {
getData(a,b) {
a.commit('getData',b)
}
},
modules: {
}
})
- 配置main.js
- 组件A
<template>
<div class="childA">
<p>我是组件A</p>
<p>{{this.$store.state.name}}</p>
</div>
</template>
<script>
export default {
data() {
return {
};
},
methods: {
}
}
</script>
<style scoped>
.childA {
margin: 0 auto;
width: 400px;
height: 170px;
border: 1px solid #000;
}
</style>
- 组件B
<template>
<div class="childB">
<p>我是组件B</p>
<p>传值到vuex中</p>
<button @click="sendData">点击传参A</button>
</div>
</template>
<script>
export default {
data() {
return {
msg:""
};
},
methods: {
sendData(){
this.$store.commit("getData",'大帅锅')
}
},
}
</script>
<style scoped>
.childB{
margin: 0 auto;
width: 400px;
height: 150px;
border:5px solid #000;
}
</style>
小结:
- 使用vuex时候记得做数据持久化(使用vuex-persist插件);
- 使用vuex推荐使用严格模式;
4.通过路由带参数进行传值
5.通过设置 Session Storage缓存的形式进行传递
6.通过provide/inject传值
7.通过 $ attrs、$ listeners传值
总结:
组件通信基本上以上7种方式;后面几种不常用就不说太多;方法很多够用就行,觉得那种方法简单就用哪种自己开心就好,有其他方法欢迎留言互相进步。