Vue如何解决兄弟组件之间传值问题

Vue
587
0
0
2023-02-01
标签   Vue组件
目录
  • 解决兄弟组件之间传值问题
  • bus可以通过两种方式来实现
  • 各类组件间传值方法(父子、兄弟、页级)
  • 父子关系组件
  • 兄弟关系组件
  • 页级关系组件

解决兄弟组件之间传值问题

vue中 父组件向子组件传递参数时,可以通过prop来传递参数,prop可以是数组形式,也可以是对象格式,子向父组件传递参数时,通过$emit来传递,$emit('方法名', 传参),来实现子组件和父组件数据交互,但是在父组件中,有好多个子组件,这些子组件怎么样来进行数据通讯呢?

有的同学会立马想到 vuex ,答案是:可以的,但是会不会大材小用?在项目中,只是个别组件需要用到通讯,我们是不会优先考虑vuex来解决的。

下面我们可以使用一个特别好用的方法来实现兄弟组件之间数据通讯

bus.js 顾名思义 巴士,所有人都可以上车,不管你在哪里,不管你是什么组件,我都可以开车过去接你。

bus可以通过两种方式来实现

1. 通过创建.js文件局部方式来实现,在需要用到的组件中引入,然后调用它即可

在项目的utils文件中创建一个js文件,在正常的项目种,会用utils来存放哪些共用的js文件,这些js文件是一些常用的方法或例如这个bus 

// bus.js
import Vue from 'vue';
export default new Vue();

接下来,哪个组件需要发车你就在哪里import给它建立一个发车点

// 在 vue组件中引入
import bus from "../../utils/bus";

然后这里我们在vue的实例中的methods函数中定义一个方法来触发这个bus发车

methods: {
      busFun(data) {
        // 通过 $emit 来触发方法,参数1 是定义方法名,参数2 是你要发送的数据
        bus.$emit('name', data)
      },
    }

这里千万要注意了,若你使用多个bus 那么参数1命名千万别重复,否则其他组件会出现无故上车的情况,这个bus中可以给无数个组件使用,但是命名不能重复,除非你创建多个bus。

接下来我们就看看这辆巴士有哪个组件要上车。

同样,如果其他组件要上车,我们必须要在这个组件中设立一个车站,不然巴士不知道要在哪里接这个组件。

// 这个组件需要上车,我们先设立一个车站
import bus from "../../utils/bus";

设立车站后,我们就需要通过 bus.$on来触发,也就是上车的技能

 created() {
      bus.$on('name', (data) => {
        console.log(data, '上车成功');
      })
    },
    // 打卡
    beforeDestroy() {
      bus.$off('name');
    },

这里会有同学会疑惑,为什么上车后需要打卡???

我们先说从上往下说吧,首先我们必须要在 vue 的created钩子函数中来触发这个bus,在页面还没加载的时候我们先触发它然后拿到数据,在created中执行,我们可以避免很多坑,例如触发bus无法拿到数据等问题(想了解的同学去问度娘,这里就不说明了)。

然后我们每次触发这个bus的时候,我们必须在整个生命周期走完的时候将这个bus给移除掉。

简单粗暴的说,就是我们每个组件上车的时候,我们必须要给它打卡掉,让后面的bus知道这个组件已经上车了,不需要在这里等待,接到就开走,头都不回的那种。

执行beforeDestroy来销毁这个bus方法,是避免方法“冗余”,若不清除这个方法,那将会出现多个bus触发,每次都会将这辆bus停留在这个vue的生命周期中,打开控制台你会发现有好多辆bus在这里停留,因为我们每次触发bus时,就会开一辆巴士出来,而不是这辆巴士一直重复开的原因。

2. 接下来我们就说一下第二种方式

// 我们在main.js中用常量创建一个bus,然后将它放入Vue实例的原型对象中。
const bus = new Vue()
Vue.prototype.$bus = bus;

然后我们就可以在全局 通过this.$bus来开巴士了(#滑稽)。接下来只要开稳就可以为所欲为了。

 created() {
      this.$bus.$on('name', (data) => {
        console.log(data, '上车成功');
      })
    },
    // 打卡
    beforeDestroy() {
      this.$bus.$off('name');
    },

最后别忘了给乘客打卡哦!

各类组件间传值方法(父子、兄弟、页级)

熟悉vue各类关系的组件之间传值方法会令开发更加得心应手,下面将对父子、兄弟、页级组件之间的传值作浅谈。

父子关系组件

- 父向子组件传值

父组件向子组件传值通常是利用props属性。首先,在子组件里定义一个props值用来接收父组件数据;然后调用子组件并v-bind绑定这个props值 = 父组件的data值。

父组件代码:

<template>
  <div class="home">
    <HelloWorld :newMsg="msg" /> 
    //绑定子组件newMsg(props值) = 父组件msg(data值)
  </div>
</template>
<script>
import HelloWorld from "@/components/HelloWorld.vue";
export default {
  name: "Home",
  data(){
    return{msg:'Welcome to Vue.js'} //父组件数据msg
  },
  components: {HelloWorld}
};
</script>

子组件代码:

<template>
  <div>
    <h1>{{ newMsg }}</h1>  //直接调用newMsg,显示“Welcome to Vue.js”
  </div>
</template>
<script>
export default {
  name: "HelloWorld",
  props: {newMsg: String}  //为子组件定义newMsg(props值)接收父组件msg
  //或者props:['newMsg']
};
</script>

- 子向父组件传值

子组件向父组件传值有两种方式:

1. 利用$emit()方法。 首先在子组件中用$emit()向上传递一个自定义事件并附带想要传递的数据;然后在父组件v-on监听这个自定义事件并自动接收到数据;最后在响应该事件的方法中进行数据操作。

子组件代码:

<script>
export default {
  name: "HelloWorld",
  data() {
    return {msg:"Welcome to Vue"} //data值,即将向上传递的值
  },
  created(){
    this.$emit('change',this.msg) //向上传递自定义事件change和data值。这里我调用created生命周期函数触发$emit()
  }
};
</script>

父组件代码:

<template>
  <div class="home">
    <HelloWorld @change="handle" /> //监听到子组件传递来的事件并响应handle方法
    <span>{{newMsg}}</span>  //直接调用newMsg,显示“Welcome to Vue”
  </div>
</template>
<script>
import HelloWorld from "@/components/HelloWorld.vue";
export default {
  name: "Home",
  data() {
    return {newMsg:""}
  },
  methods:{
    handle(msg){           //定义handle方法,将自动接收到的msg值给了自己的newMsg
      this.newMsg = msg
    }
  },
  components: {HelloWorld}
};
</script>

2.利用ref属性。 ref是vue提供的内部属性,它其实相当于类似"id""class"的标识。首先在子组件上定义ref值(例:ref="son");然后在父组件中用this.\$refs.son即可获取到son这个ref的组件,进行操作。

子组件代码:

<script>
export default {
  name: "HelloWorld",
  data() {
    return {msg:"Welcome to Vue"}  //即将向父传递的data值
  }
};
</script>

父组件代码:

<template>
  <div class="home">
    <HelloWorld ref="son" />  //在子组件上定义一个ref 名为"son"
  </div>
</template>
<script>
import HelloWorld from "@/components/HelloWorld.vue";
export default {
  name: "Home",
  mounted(){
    console.log(this.$refs.son.msg)  //获取到ref名为"son"的子组件内msg数据,在控制台显示“Welcome to Vue”
  },
  components:{HelloWorld}
};
</script>

兄弟关系组件

兄弟组件间的传值方式我称为 “总线转送” 。原理很简单,利用父组件作为总线去转送数据。首先,用上述讲到的ref属性或$emit()将组件A的数据向上传递给父组件;再由父组件通过props属性向下传值到组件B。

组件A代码:

<template>
  <div>click me</div>
</template>
<script>
export default {
  name: "HelloWorld",
  data() {
    return {helloMsg:"Welcome to Vue"} //即将传送的数据helloMsg
  }
};
</script>

组件B代码:

<template>
  <div>
    {{hiMsg}}  //直接调用hiMsg,显示“Welcome to Vue”
  </div>
</template>
<script>
export default {
  name: "Hi",
  props:{hiMsg:String} //用来接收父组件传值的props属性hiMsg
};
</script>

父组件代码:

<template>
  <div class="home">
    <HelloWorld ref="son" @click.native="change"></HelloWorld> //定义ref属性接收组件A传来的数据;点击组件A触发change()
    <Hi :hiMsg="msg"></Hi>  //绑定组件B的hiMsg=msg,将数据转发给组件B
  </div>
</template>
<script>
import HelloWorld from "@/components/HelloWorld.vue";
import Hi from "@/components/Hi.vue";
export default {
  name: "Home",
  data(){
    return{ msg: "" }
  },
  methods:{
    change(){
      return this.msg = this.$refs.son.helloMsg //将组件A传来的helloMsg赋予this.msg
    }
  },
  components:{HelloWorld, Hi}
};
</script>

页级关系组件

在vuecli3.0以后,项目结构对组件级别作了明确的区别:入口组件(App) - 页级组件(View) - 零件组件(Component)。一般在开发中我们也会尽量按照这种结构去划分模块,但页级组件没有父组件,如果说页级组件之间需要传值,那就不能用“总线转送”的方法了。

vuex:这是官方提供的一个插件,可称为“状态管理仓库”,用来管理项目中需要跨页共享反复调用的状态(状态你可以理解成就是数据、变量、方法)。

vuex的存放仓库为Store,内部结构可分五大块:

  • state:状态库,寄放基础状态。
  • mutations:同步方法,可理解成相当于组件内的computed属性,接收state进行改装。
  • actions:异步方法,只针对mutations,接收并改装。
  • getters:过滤器,对state调用前进行改装、派生并返回数据。
  • modules:模块,在复杂项目中用modules处理Store,使分工更清晰。

vuex令开发中数据共享操作更得心应手,还需要大家到官方文档认真学习。