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]等方法,不過不太常用,大家可以自行了解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章