vue:父子组件的通信

上一篇:vue组件化开发

在上一篇文章中,我们申明了一点,那就是组件之间是独立的,除非构建了通信。

组件间为什么要构建通信?

在单一文件中写上整个网站的代码逻辑,这无疑使得维护成本巨大。所以我们选择了组件化开发,把组件间独立起来,这样谁也不干涉谁,是谁的逻辑就在谁的文件中实现,最后串在一起,一个网站内容就大功告成了。那父子组件的通信有什么用呢?

我们不妨设想一个场景:在点外卖时,当我们选中某个餐品,下面的计价开始变化,甚至还会计算一下是否满配送,在淘宝购物车中,当我们清空一些商品时,下面也会有总价的变化,或者如果我们想要两个相同组件却拥有不同的反馈,比如两个按钮,一个按钮为提交,一个按钮为回退,但他们来自同一个组件,该怎么为他们添加事件呢?

以上的所有情况我们都可以通过父级组件为子组件提供数据来完成,通过props我们可以为子组件添加Number,String,Object,Array,甚至Function。并且,增加了组件间的通信也表现出了vue的弹性,我们是可以根据自己意愿来添加组件联系的。

组件的通信实现

想要实现组件间的通信,得有一方得发出信息,另一方接收信息,然后返回信息使对方接收到信息,这就是双向通信,但是不是每个组件都是双向通信,我们也可以父级组件单传数据,也可以使子组件单传数据给父组件。

父组件单传子组件

父组件

<template>
  <show-info name="Mike" :age="18" :height="1.88" />
</template>

<script>

import ShowInfo from './ShowInfo.vue'
export default {
  components:{
    ShowInfo,
  }
}
</script>

<style scoped>

</style>

子组件

<template>
  <div class="infos">
    <h2>姓名:{{name}}</h2>
    <h2>年龄:{{age}}</h2>
    <h2>身高:{{height}}</h2>
  </div>
</template>

<script>
export default {
  props:["name","height","age"],
  data(){
    return{
    }
  },
}
</script>

<style scoped>

</style>

 首先,在传输数据前,我们是不是得对一个暗号?这个暗号就是属性名,父组件动态绑定的属性名要与子组件props中的一致,无关顺序。子组件获得来自父组件的数据后就可以使用了。当然,props不只有数组表达形式,还有对象形式。

<template>
  <div class="infos">
    <h2>姓名:{{name}}</h2>
    <h2>年龄:{{age}}</h2>
    <h2>身高:{{height}}</h2>
  </div>
</template>

<script>
export default {
  props:{
      name:String,
      height:Number,
      age:Number
   },
  
  data(){
    return{
    }
  },
 
}
</script>

<style scoped>

</style>

props对象形式中支持我们为传入的数据类型进行一个限制,但是这种限制比较弱,只会提醒一下你,而不会报错。props还有一种形式,这种形式更加完备,我们可以为传输的数据添加默认值,这样即使父级组件没有传入数据也会正常渲染。

<template>
  <div class="infos">
    <h2>姓名:{{name}}</h2>
    <h2>年龄:{{age}}</h2>
    <h2>身高:{{height}}</h2>
  </div>
</template>

<script>
export default {
  props:{
    name:{
      type:String,
      default:"我是默认值"
    },
    height:{
      type: Number,
      default: 0
    },
    age:{
      type: Number,
      default: 0
    },
     
  },
  data(){
    return{
    }
  },
}
</script>

<style scoped>

</style>

非props

形象来说,就是没对上暗号的那些数据该怎么办?

在vue中,这些数据并不会直接丢掉,而是挂在子组件的根节点上,如果有两个根节点会发生什么?

什么都不会发生,会报个错然后让你选取一个根节点来挂载非props数据。

我们可以使用$attrs来访问所有的非props的属性。 

子组件单传父组件

自定义事件

App.vue

<template>
  <h2>counter:{{ counter }}</h2>
  <add-counter @add="addBtnClick"></add-counter>
  <sub-counter @sub="subBtnClick"></sub-counter>
</template>

<script>
import AddCounter from './AddCounter.vue'
import SubCounter from './SubCounter.vue'
export default {
  components:{
    AddCounter,
    SubCounter
  },
  data(){
    return {
      counter:0
    }
  },
  methods:{
    addBtnClick(count){
      this.counter += count;
    },
    subBtnClick(count){
      this.counter -= count;
    }
  }
}
</script>

<style scoped>

</style>

 addCounter.vue

<template>
  <div class="add">
    <button @click="addCounter(1)">+1</button>
    <button @click="addCounter(5)">+5</button>
    <button @click="addCounter(10)">+10</button>
  </div>
</template>

<script>
export default {
  emits:["add"],
  methods:{
    addCounter(count){
      console.log("btnClick",count);
      //第一个参数为事件名称,第二个参数为传递参数
      this.$emit("add",count);
    }
  }
}
</script>

<style scoped>

</style>

 subCounter.vue

<template>
  <div class="sub">
    <button @click="subCounter(1)">-1</button>
    <button @click="subCounter(5)">-5</button>
    <button @click="subCounter(10)">-10</button>
  </div>
</template>

<script>
export default {
  emits:["sub"],
  methods:{
    subCounter(count){
      console.log("btnClick",count);
      this.$emit("sub",count);
    }
  }
}
</script>

<style scoped>

</style>

先来简述一下上述代码功能:

我们分别有+1,+5,+10的三个按钮以及与之对应的减法按钮,当我们点击这些按钮后,位于父组件的counter发生相应的变化。

首先,我们想到的就是在子组件addCounter中创建一个方法increment,传入参数,可以控制为counter加一定的值,再在subCounter中创建一个方法decrement,传入参数,控制counter减一定的值。而counter我们可以通过父组件传过来,计算后的counter在写个方法让父组件监听,父组件得到值后再赋值给counter渲染。

这个方法是可行的。代码如下:

App.vue

<template>
  <div class="app">
    <h2>{{ counter }}</h2>
    <increment :counter="counter" @back="getBackValue"></increment>
  </div>
</template>

<script>
import Increment from './Increment.vue';
export default{
  components:{
    Increment
  },
  data(){
    return{
      counter:0
    }
  },
  methods:{
    getBackValue(res){
      this.counter = res
    }
  }
}
</script>

<style scoped>

</style>

increment.vue

<template>
  <div class="add">
    <button @click="addCounter(1)">+1</button>
    <button @click="addCounter(5)">+5</button>
    <button @click="addCounter(10)">+10</button>
  </div>
</template>

<script>
export default {
  emits:["back"],
  props:["counter"],
  data(){
    return {
      
    }
  },
  methods:{
    addCounter(count){
     let value = this.$props.counter;
     value += count
     console.log("Backing")
     this.$emit("back",value)
    },
    backValue(){
      
    }
  }
}
</script>

<style scoped>

</style>

这样的确能实现目的,但是却不建议这么做,这相当于是把运算过程放在了子组件中,仅仅是把结果返回给父组件。但在实际开发中,我们不一定只开发这么简单的功能,运算或许没这么简单,况且,按下+1按钮给我返回1才符合逻辑吧?我们并不需要子组件干这么多不属于它的活。

下面是另外的一种思路:

对于子组件,不需要父组件给我任何数据,我只需要知道用户按下了哪一个按钮任何返回那个按钮的数值,这样我只需要在点击按钮中释放事件并且把数值传给释放事件中,父组件只需要监听到我这个事件,那任务就结束了。父组件接到子组件传来的值,这时,它可以对这个值做任何操作,这就使得我们有更多的操作空间。

而这就对应着一开始的代码,对比两种思路的代码,你会发现第二种思路的方式组件之间更加独立,至少我们不需要改逻辑的时候跑到子组件的方法中改。

props接收函数的情况

父级组件

<template>
  <div>
    <h1>我是父组件</h1>
    <ChildComponent :handle-click="methodA" method-name="methodA" />
    <ChildComponent :handle-click="methodB" method-name="methodB" />
  </div>
</template>

<script setup>
import ChildComponent from './components/ChildComponent.vue';

const methodA = (data) => {
  console.log('methodA 被调用:', data);
};

const methodB = (data) => {
  console.log('methodB 被调用:', data);
};

</script>

 子组件

<template>
  <div>
    <button @click="callMethod">点击我,调用父组件的方法</button>
  </div>
</template>

<script setup>
import { defineProps } from 'vue';

const props = defineProps({
  handleClick: {
    type: Function,
    required: true,
  },
  methodName: {
    type: String,
    required: true,
  },
});

function callMethod() {
  props.handleClick("一些子组件的数据")
}

</script>

这结合了后面composition api的内容,但大体是一样的,这里我们做出的效果就是相同的两个组件拥有不同的方法,我们向子组件传入函数,这个用传统方法一直搞不出来,难受。最后只能用composition api来替代了,我有时间再试试。回到正题,我们传入methodA,子组件接收到这个函数,当点击按钮时,就会调用接收的函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值