vue中非父子组件传值

我们在之前的文章中提到过,父组件向子组件传值时是利用定义属性来传值的,而子组件向父组件传值是通过事件,那么下面的情况,同级组件或是隔代组件,即非父子组件之间的传值是怎么进行的呢(如下图)?
在这里插入图片描述
一般来说我们有两种解决非父子组件传值的问题:

  1. 发布订阅模式/观察者模式/bus/总线机制
  2. vuex
    在这里我们主要对总线机制进行一下学习,关于vuex的内容随后学习补充。
    – 首先创建一个HTML文件,并创建父子组件,
<html>
<head>
  <meta charset="UTF-8">
  <title>非父子组件传值</title>
  <script src="../node_modules/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
  <child></child>
  <child></child>
</div>
<script>
  //子组件
  Vue.component('child', {
    template: '<div>child</div>'
  });

  var vm = new Vue({
    el: '#root'
  })
</script>
</body>

</html>

页面效果如下:
在这里插入图片描述
我们希望子组件显示内容是外部传递进来的,这里涉及到了父到子的参数传递:

<div id="root">
  <child content="hello"></child>
  <child content="world"></child>
</div>

– 然后子组件通过props进行接收并做一个参数校验,传来的content的值必须为String类型,接下来我们就可以在子组件中显示content的内容了:

//子组件
  Vue.component('child', {
    props: {
      content: String
    },
    template: '<div>{{content}}</div>'
  });

页面效果:
在这里插入图片描述
– 接下来进一步实现效果点击hello,world也变成hello,反之亦然。这里就涉及到非父子组件之间的传值了,上面的子组件被点击的时候,要将hello传值给下面同级的子组件(兄弟组件),反之亦然。

– 首先我们new一个vue的实例,然后将Vue.prototype上的bus属性指向这个实例,这样我们每次调用Vue或者是创建组件的时候,组件上面都会挂载bus这个属性:

Vue.prototype.bus = new Vue();

– 接下来在子组件中绑定一个点击事件handleClick,并在methods中定义该事件:

Vue.component('child', {
    props: {
      content: String
    },
    template: '<div @click="handleClick">{{content}}</div>',
    methods:{
      handleClick: function () {
        alert(this.content);
      }
    }
  });

在这里插入图片描述
– 接下来要将点击的组件的参数传递给另外一个组件,bus是vue的实例,所以也具有$emit()这个方法,我们可以通过bus来向外派发一个事件,同时携带了参数this.content,就是点击的组件的内容:

handleClick: function () {
        this.bus.$emit('change',this.content)
      }

– 其他组件要对这个事件进行监听,我们借助一个生命周期钩子:

mounted: function () {
      this.bus.$on('change', function (msg) {
        alert(msg);
      })
    }

在这里插入图片描述
该组件的值会弹出两次,这是因为在一个child组件中触发事件的时候,两个child组件都会进行监听,所以两个子组件都会弹出弹框。

– 这个时候我们只需把监听的子组件内容变成msg:

mounted: function () {
      this.bus.$on('change', function (msg) {
        this.content = msg;
      })
    }

但是我们会发现点击根本没有效果,这是因为function中this的作用域发生了变化,需要将this保存:

mounted: function () {
      var that=this;
      this.bus.$on('change', function (msg) {
        that.content = msg;
      })
    }

– 但是点击控制台出现了警告信息:
在这里插入图片描述
我们在之前说过单向数据流的问题,子组件不能对父组件传来的参数直接进行操作,最好创建一个副本:

data:function() {
      return {
        name:this.content  /*定义一个content的副本name,单向数据流*/
      }
    },

然后将所有的content都换成name。
最后附上完整的代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>非父子组件传值的问题(Bus/总线/发布订阅模式/观察者模式)</title>
  <script src="../node_modules/vue/dist/vue.js"></script>
</head>
<body>
  <div id="root">
    <child content="dell"></child>
    <child content="lee"></child>
  </div>
  <script>
    /*需求:点击Dell时会变成Lee,点击Lee时会变成Dell*/
    Vue.prototype.bus=new Vue()  /*每个实例上都会挂载一个bus属性,bus又是一个vue实例*/
    Vue.component('child',{
      data:function() {
        return {
          name:this.content  /*定义一个content的副本name,单向数据流*/
        }
      },
      props:{
        content:String
      },
      template:'<div @click="handleClick">{{name}}</div>',
      methods:{
        handleClick:function () {
          this.bus.$emit('change',this.name)
        }
      },
      mounted:function () {  /*元素挂载到页面上才会执行这个函数*/
        var that=this  /*此时this的作用域发生了变化*/
        this.bus.$on('change',function (msg) {   /*能够监听到bus上发布的事件*/
         /* alert(msg)  /!*会弹出两次,因为在一个child组件上触发事件的时候,
         两个组件都进行了同一事件的监听*!/*/
          that.name=msg
        })
      }
    })

    var vm=new Vue({
      el:"#root",

    })
  </script>
</body>
</html>

这样通过一个bus总线就实现了非父子组件中的传值。

猜你喜欢

转载自blog.csdn.net/zl13015214442/article/details/87900401