vue组件间父子传值、祖父子孙传值和任意传值的5种方法,prop,emit,attrs,listeners以及中央事件总线

笔者今天自学vue组件间的传值时非常混乱,故在此整理一下。


父子传值

prop: 父向子传值

步骤:

  • 在子部件标签中加入自定义属性:
    <Son :fatherMsg="msg"/>
  • 在子部件中添加props数组:
    props:['fatherMsg']
let Son={
  props:['fatherMsg'],
  template: `
    <div>
    <h2 class="display-4 text-secondary">This is Son</h2>
    <p class="text-danger">{{fatherMsg}}</p>
    </div>`
};

let Father = new Vue({
  el:'#app',
  data(){
    return{
      msg:'A message from father.'
    }
  },
  components:{
    Son
  },
  template:`
    <div>
      <h2 class="display-4 text-primary">This is Father</h2>
      <Son :fatherMsg="msg"/>
    </div>`
});

在这里插入图片描述

$emit:子向父传值

子传父与父传子略有不同,依赖于事件,我们定义一个按钮用于触发事件。

步骤:

  • 准备发送数据的方法
    methods: {sendMsg(){this.$emit('sendMsgFromSon',this.msg);}},
  • 准备一个触发事件的对象并指定发送数据的方法(如button)
    <button class="btn btn-success" @click="sendMsg">Send</button>
  • 预留一个接收数据的参数
    sonMsg: 'Message does not arrive.'
  • 准备接收数据的方法并修改参数
    methods: {getMsg(resp) {this.sonMsg = resp;}},
  • 在子部件中加入自定义属性接收数据并指定接收数据的方法
    <Son @sendMsgFromSon="getMsg"/>
  let Son = {
    data() {
      return {
        msg: 'A message from son.'
      }
    },
    methods: {
      sendMsg() {
        this.$emit('sendMsgFromSon', this.msg);
      }
    },
    template: `
      <div>
      <h2 class="display-4 text-secondary">This is Son</h2>
      <button class="btn btn-success" @click="sendMsg">Send</button>
      </div>`,
  };

  let Father = new Vue({
    el: '#app',
    data() {
      return {
        sonMsg: 'Message does not arrive.'
      }
    },
    components: {
      Son
    },
    methods: {
      getMsg(resp) {
        this.sonMsg = resp;
      }
    },
    template: `
      <div>
        <h2 class="display-4 text-primary">This is Father</h2>
        <p class="text-danger">{{sonMsg}}</p>
        <Son @sendMsgFromSon="getMsg"/>
      </div>`
  });

在这里插入图片描述


祖父子孙传值

有了父子传递的经验,那么多层传递是否也可以用多个prop和emit实现呢?

当然可以,不过这显得太麻烦了。于是,vue2.4推出了$attrs$listeners帮我们解决了这个问题。

$attrs: 祖父向子孙传值

不然发现,此处的Father和prop例子中的完全相同,Grandson也与Son除名字外完全相同,唯一的区别是多了中间组件。

关键步骤:

  • 中间组件中添加子孙组件为子组件
    components:{Grandson},
  • 使用v-bind为子组件绑定$attrs
    <Grandson v-bind="$attrs"/>
let Grandson = {
  props: ['fatherMsg'],
  template: `
    <div>
      <h2 class="display-4 text-success">This is Grandson</h2>
      <p class="text-danger">{{fatherMsg}}</p>
  </div>`
};

let Son = ({
  components: {
    Grandson
   },
  template: `
    <div>
      <h2 class="display-4 text-secondary">This is Son</h2>
      <Grandson v-bind="$attrs"/>
    </div>`
});

let Father = new Vue({
  el: '#app',
  data() {
    return {
      msg: 'A message from father.'
    }
  },
  components: {   
    Son
  },
  template: `
    <div>
      <h2 class = "display-4 text-primary"> This is Father </h2>
      <Son :fatherMsg="msg"/>
    </div>`
});

在这里插入图片描述

$listeners: 子孙向祖父传值

有了祖父传子孙的经验,子孙传祖父也大同小异,只需在子传父的基础上添加即可:

  • 为中间组件添加子孙组件为子组件
    components:{Grandson},
  • 使用v-on为子组件绑定$listeners
    <Grandson v-on="$listeners">
let Grandson = {
   data() {
    return {
      msg: 'A message from Grandson.'
    }
  },
  methods: {
    sendMsg() {
      this.$emit('sendMsgFromGrandson', this.msg);
    }
  },
  template: `
    <div>
    <h2 class="display-4 text-success">This is Grandson</h2>
    <button class="btn btn-success" @click="sendMsg">Send</button>
    </div>`,
};

let Son = {
  components: {
    Grandson
  },
  template: `
  <div>
  <h2 class="display-4 text-secondary">This is Son</h2>
  <Grandson v-on="$listeners"/>
  </div>`
};

let Father = new Vue({
  el: '#app',
  data() {
    return {
       grandsonMsg: 'Message does not arrive.'
    }
  },
  components: {
    Son
  },
  methods: {
    getMsg(resp) {
      this.grandsonMsg = resp;
    }
  },
  template: `
    <div>
      <h2 class="display-4 text-primary">This is Father</h2>
      <p class="text-danger">{{grandsonMsg}}</p>
      <Son @sendMsgFromGrandson="getMsg"/>
   </div>`
});

在这里插入图片描述

任意传值

中央事件总线:任意组件间传值

最后的方法叫做中央事件总线,它可以实现任意组件的传值,无论是父子、祖父子孙、还是兄弟。

这里我们创建了一个全局的bus,这个bus就像一个接线员,接收一个数据,再发送给别的组件。

步骤:

  • 声明一个全局的Vue作为总线
    let bus = new Vue();
  • 为发送数据的组件添加方法,使用bus.$emit发送
    methods:{sendMsg(){bus.$emit('msgFromComponent1', this.msg);}},
  • 为发送数据的组件提供事件触发方法(此处使用按钮):
    <button class="btn btn-success" @click="sendMsg">Send</button>
  • 为接收的数据预留槽位
    data(){return{msg: 'Message does not arrive.'}},
  • 为接收数据的组件提供方法,使用bus.$on接收(此处使用mounted,意味着数据改变就调用方法,从而实现同步更新)
    mounted() {bus.$on('msgFromComponent1', (resp) => {this.msg = resp;})},
let bus = new Vue();

let Component1 = new Vue({
  el: '#app',
  data() {
    return {
      msg: 'A message from component1.'
    }
  },
  methods: {
    sendMsg() {
      bus.$emit('msgFromComponent1', this.msg);
    }
  },
  template: `
    <div>
      <h2 class="display-4 text-primary">This is Component1</h2>
      <button class="btn btn-success" @click="sendMsg">Send</button>
    </div>`
});

let Component2 = new Vue({
  el: '#app2',
  data() {
    return {
      msg: 'Message does not arrive.'
    }
  },
  mounted() {
    bus.$on('msgFromComponent1', (resp) => {
      this.msg = resp;
    })
  },
  template: `
   <div>
     <h2 class="display-4 text-secondary">This is Component2</h2>
     <p class="text-danger">{{msg}}</p>
   </div>`
})

在这里插入图片描述
以上5种方法讲解完成,除此之外,还有provided / inject$parent / $children[index]等方法,不过不太常用,大家可以自行了解。

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值