分享vue 组件间通信7种方法(父子组件间传参,兄弟组件传参)

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>

小结:

  1. 这种和 2.1 通过自定义事件(emit)从子组件向父组件中传递数据有区别,两者有着父子组件关系上的不同;
  2. 这种传参父可以改变子(数据), 但子不能直接改变父(数据), 父中数据的变动只能由它自己决定;
  3. 不要通过在子组件中修改引用类型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步:

  1. 定义一个新的vue实例专门用于传递数据,并导出;
  2. 定义传递的方法名和传输内容,点击事件或钩子函数触发eventBus.emit事件
  3. 接收传递过来的数据

注意:

	1. enentBus是一个另一个新的Vue实例,区分两个this所代表得vue实例;
	2.  在开发时候要记得解绑enentBus; 
created () {
    // 解绑enentBus
    EventBus.$off('eventDataA')
}

3.1通过vuex进行传值

  1. 先要下载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: {
  }
})


  1. 配置main.js
    在这里插入图片描述
  2. 组件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>
  1. 组件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>

小结:

  1. 使用vuex时候记得做数据持久化(使用vuex-persist插件);
  2. 使用vuex推荐使用严格模式

4.通过路由带参数进行传值

5.通过设置 Session Storage缓存的形式进行传递

6.通过provide/inject传值

7.通过 $ attrs、$ listeners传值

总结:

组件通信基本上以上7种方式;后面几种不常用就不说太多;方法很多够用就行,觉得那种方法简单就用哪种自己开心就好,有其他方法欢迎留言互相进步。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值