iT邦幫忙

2021 iThome 鐵人賽

DAY 21
0
自我挑戰組

Vue.js 從零開始系列 第 21

Vue.js 從零開始:emit 元件的溝通

  • 分享至 

  • xImage
  •  

上一篇已經學會如何把外部元件的資料傳到內部元件,如果是內部傳到外部,就需要透過emit來達成。

Props in, Ebent out
https://ithelp.ithome.com.tw/upload/images/20211001/20118347UjfpBRcyfE.png

emit 傳遞事件

emit除了可以傳遞資料,也可以只傳遞事件,來觸發外部的事件:

<div id="app">
  {{ text }}:
  <button type="button" @click="add()">add</button>
  {{ num }}
  <br>
  <br>
  <out-text @button-click="add"></out-text>
</div>
const app = Vue.createApp({
  data() {
    return {
      text: "外部元件",
      num: 0,
    }
  },
  methods: {
    add() {
      this.num++;
    }
  }
});
app.component("outText",{
  template: `<button @click="click">emit add</button>`,
  data() {
    return {
      text: "內部元件"
    };
  },
  methods: {
    click() {
      this.$emit('buttonClick');
    }
  }
});
app.mount("#app");

emit起手式:

  1. 定義外層接收的函式 add()
  2. 定義內層 $emit 觸發的函式
  3. 透過外層模板 @button-click="add" 來觸發外層函式

@button-click="add"Props一樣記法前內後外,撰寫時可以按照步驟console.log,會比較順利完成。


emit 傳遞參數

將內層資料傳遞到外層元件上使用:

<div id="app">
  <h3> {{ title }} </h3>
  {{ text }}:
  <button type="button" @click="add">add</button>
  {{ num }}
  <br>
  <br>
  <out-text @button-click="add"></out-text>
</div>
const app = Vue.createApp({
  data() {
    return {
      title: "",
      text: "外部元件",
      num: 0,
    }
  },
  methods: {
    add(addtext) {
      this.num++;
      this.title = addtext;
      //addtext 沒有值傳起來就會變成 事件物件 [object PointerEvent]
    }
  }
});
app.component("outText",{
  template: `<button @click="click">emit add</button>`,
  data() {
    return {
      text: "內部元件",
      insideText: "內部元件傳遞文字"
    };
  },
  methods: {
    click() {
      this.$emit('buttonClick', this.insideText);
    }
  }
});
app.mount("#app");

https://ithelp.ithome.com.tw/upload/images/20211001/20118347XO7vRrxNm4.png

因為要帶參數所以原本$emit裡要多帶一個this.insideText參數,透過外層模板@button-click="add",傳遞到外層元件的add(addtext),最後再傳回外層data渲染畫面。


emits 驗證

<div id="app">
  {{ text }}:{{ num }}
  <br>
  <br>
  <out-text @button-click="add"></out-text>
</div>
const app = Vue.createApp({
  data() {
    return {
      text: "外部 num",
      num: 0,
    }
  },
  methods: {
    add(num) {
      this.num = this.num + num;
    }
  }
});
app.component("outText",{
  template: `{{ text }}:<button @click="$emit('buttonClick', num)">emit add</button>`,
  data() {
    return {
      text: "內部元件",
      num: 3,
    };
  },
});
app.mount("#app");

內層元件除了使用methods的方式撰寫emit,也可以直接寫在template@click="$emit('buttonClick', num)"裡面的'buttonClick'就是內層的@button-clicknum則是內層datanum。當我們開啟開發者工具Console,會發現跳出這一段警告:
https://ithelp.ithome.com.tw/upload/images/20211002/20118347FjDKWjNgY6.png
這是因為無法正確追蹤你帶入的變數內層的num,就會跳出這個警告提示,這時只要加上emits:['buttonClick']就能消除警告。

emits正確用法是拿來驗證事件參數傳出去的值,是否符合預期的型別:

<div id="app">
  {{ text }}:{{ num }}
  <br>
  <br>
  <out-text @button-click="add"></out-text>
  <br>
  <out-text2 @button-click="add"></out-text2>
</div>
const app = Vue.createApp({
  data() {
    return {
      text: "外部 num",
      num: 0,
    }
  },
  methods: {
    add(num) {
      this.num = this.num + num;
    }
  }
});
app.component("outText",{
  template: `{{ text }}:<button @click="$emit('buttonClick', num)">emit add</button>`,
  data() {
    return {
      text: "內部元件",
      num: 3,
    };
  },
  emits: ['buttonClick']
});
app.component("outText2",{
  template: `{{ text }}:<button @click="$emit('buttonClick', '1')">test</button>`,
  data() {
    return {
      text: "emits",
      num: 3,
    };
  },
  emits: {
    buttonClick: (num) => {
      if (typeof num !== 'string') {
        console.warn('buttonClick 事件參數型別須為 String')
      }
      return typeof num === 'string'
    }
  }
});
app.mount("#app");

觀察outText2裡面的$emit帶的參數是一個字串1,而emits要改為物件,把要觀察的事件帶入buttonClick: (num)(num)就是$emit('buttonClick', '1')的參數,如果參數的型別是字串的話就不會跳出警告,可以試著更改參數型別,警告提示就會顯示:[Vue warn]: Invalid event arguments: event validation failed for event 'buttonClick',事件驗證失敗,就可以知道帶出去的參數是否符合預期。

以上如有錯誤,歡迎指教。

/images/emoticon/emoticon41.gif


參考資料

六角學院
重新認識 Vue.js


上一篇
Vue.js 從零開始:props 元件的溝通
下一篇
Vue.js 從零開始:Slot
系列文
Vue.js 從零開始30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言