iT邦幫忙

2022 iThome 鐵人賽

DAY 13
0
Modern Web

真的好想離開 Vue 3 新手村 feat. CompositionAPI系列 第 13

真的好想離開 Vue 3 新手村 - Day 13: v-on 語法、修飾符與找不到的 console.log

  • 分享至 

  • xImage
  •  

前言

今天這篇會先介紹 v-on 的語法和修飾符,熟悉的人可以考慮跳過這個部份

再來會提到自己在學習 v-on 時的疑惑:

「為什麼不能在 inline handler 使用 console.log?」

如果你也很好奇,就一起來看看吧! (不好奇的話就跳過吧哈哈)

最後會補充 .passive 修飾符,這個修飾符對應到 addEventListener 的 passive option,是我自己之前比較陌生的部份!

Outline

  • v-on 快速通關:語法簡介
  • 為什麼不能在 inline handler 調用 console.log
  • 如何設定在 Vue 模板可使用 console.log
    • 利用 app.config.globalProperties 自定義所有模板都可以取到的全域屬性
  • 補充:來點原生 Javascript 之 .passive 修飾符

v-on 快速通關

Syntax

  • v-on

    • v-on 指令是用來為元素綁定事件監聽器
    • v-on 的部份可以縮寫為 @
  • Argument

    • 原生事件:對應原生 Event 底下的 type 屬性,鍵的名稱可以參考 MDN - Events
    • 自訂事件:可以透過 emit 來發送自定義事件
  • Modifiers
    Vue 針對常用的事件邏輯處理,提供對應的修飾符(modifier),開發者不用一直寫重複的程式碼,也讓 handler 內的程式碼變得更簡潔,並且修飾符可以串接使用。

    • .stop:阻止事件冒泡,相當於 event.stopPropagation()
    • .prevent:取消事件的預設行為,相當於 event.preventDefault()
    • .self:只在綁定監聽器的元素&觸發監聽器的元素相等才執行,即 event.target === event.currentTarget

    還有對應到 addEventListener 三個 option 的修飾符:

    addEventListener(type, listener, options = {
        capture: true,
        once: true,
        passive: true,
    })
    
    • .capture: 切換成捕獲模式(capture)
    • .once:只觸發一次,觸發後就自動移除監聽
    • .passive:告訴瀏覽器這個 handler 內不會呼叫 event.preventDefault(),通常用在 touch 相關的事件,可以讓行動裝置上的頁面滑動更流暢;不可以和 .prevent 修飾符同時使用。(發現自己對 .passive 並不熟悉,這部份補充在最後)

    按鍵修飾符:監聽鍵盤相關事件時,通常需要確認使用的按鍵,在 Vue 可以透過 Key modifier 做到,鍵的名稱可以參考 MDN - Key values for keyboard events
    針對常用的按鍵提供別名:

    • .enter
    • .tab
    • .delete (包含 "Delete" 和 "Backspace")
    • .esc
    • .space
    • .up
    • .down
    • .left
    • .right

    系統鍵修飾符:和一般按鍵不同,事件觸發時,系統鍵必須處於按下的狀態,通常會跟其他按鍵組合使用。

    • .ctrl
    • .alt
    • .shift
    • .meta

    滑鼠按鍵修飾符:.left.right.middle 可以配合 mouseEvent 使用。

    嚴格修飾符 .exact:想要嚴格符合設定才會觸發事件,可以使用 .exact 來做修飾,以 click 為例:

    • @click.exact : 什麼按紐都不按點擊按鈕才會觸發事件。
    • @click : 在點擊時按住任何按鈕都會觸發事件。
  • Value
    v-on 根據綁定的 value 不同分為兩種:

    1. method handler:value 指向 script 內定義的 method
      • 會自動傳入 event 作為第一個參數
      <button @click="warn">
        Submit
      </button>
      
      function warn(event) {
        // 可以取到原生的 event 物件
        if (event) {
          event.preventDefault()
        }
        window.alert("現在還不能送出");
      }
      
    2. inline handler:指令綁定要執行的 Javascript 內容
      • 一般使用
      <button @click="count++">+1</button>
      <button @click="userInput = ''">Reset</button>
      
      • 呼叫需要傳參數的 method
      function warn(message, event) {
        // 可以取到原生的 event 物件
        if (event) {
          event.preventDefault()
        }
        alert(message)
      }
      
      1. 一般函式 + $event
      <button @click="warn('Form cannot be submitted yet.', $event)">
        Submit
      </button>
      
      1. 箭頭函式 + event
      <button @click="(event) => warn('Form cannot be submitted yet.', event)">
        Submit
      </button>
      

為什麼不能在 inline handler 用 console.log

在 Vue 模板測試 v-on event handler,如:在父元件監聽子元件 emit 事件時,會很直覺想用 inline handler 搭配 console.log() 去印東西,確認有監聽到事件。

像這樣:

<template>
    <button @click="console.log('點擊了按鈕')">按鈕</button>
</template>

或是這樣:

<template>
    <BaseInput
        :modelValue="userName"
        @update:modelValue="console.log('輸入了名字')"
      />
</template>

結果就是收到無數的報錯QQ
像這樣:

原因

這是因為 Vue 預設 template 中的表達式只能使用部份的全域物件,例如:Number, Array, Math, Date 等等,其中不包括 consolewindow

所以每次在 template 讀到 console.log 時, Vue 無法取用到全域的 console,它會在元件內部找尋 console 這個變數,所以相關報錯通常會是無法讀取 undefined(reading 'log')。
同理,在 Vue 預設的情境下,我們也沒辦法在 template 調用 window.alert(),或 window 底下相關的變數或函式。

但我們可以透過 app 實例調整設定,做法很簡單。

最後附上 Vue template 可以使用的完整 global 清單如下: 原始碼連結

const GLOBALS_WHITE_LISTED =
  'Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,' +
  'decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,' +
  'Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt'

設定在 Vue 模板可使用 console.log

我們可以透過 app.config.globalPropertiesconsole 註冊為所有元件實例都可以取用到的全域物件。

main.js 中加入這段程式碼,就可以在所有 Vue template 中使用 console.log

app.config.globalProperties.console = console;

記得要在 app 實例建立之後,才可以透過 .config 去設定。

const app = createApp(App);

app.config.globalProperties.console = console;

app.mount("#app");

其他注意事項

  1. 如果全域屬性和元件上的屬性有衝突,會以元件的屬性為優先
  2. 這個是 Vue 3 的用法,如果你使用的是 Vue 2 ,要透過 Vue.property 去設定全域屬性。

補充

來點原生 Javascript 之 .passive 修飾符

.passive 修飾符會對應到 addEventListener 的 passive option (預設值為 false):

addEventListener(type, listener, {
    passive: true,
})

將 passive 選項設置為 true 就是在告訴瀏覽器:這個事件 handler 裡面不會呼叫 event.preventDefault()你可以直接響應預設行為

這樣有什麼好處?

可以優化效能,提昇使用者體驗。
因為瀏覽器事先不知道 handler 裡面是否會呼叫 event.preventDefault() 來阻止預設行為,所以瀏覽器需要先執行一次 handler,花時間確定裡面有沒有呼叫 event.preventDefault() ,才決定是否要響應預設行為

適合拿來優化高頻率觸發的事件監聽器,例如:手機的 touch 事件,瀏覽器就不用在每次使用者開始滑動的時候,花時間重新確認 handler 裡面是否呼叫 event.preventDefault(),導致滑動上的微卡頓。

想知道效果可以看這個影片:
Yes

  • 左邊:一般預設
  • 右邊:啟用 passive option

根據 MDN 的說法,除了 Safari 和 IE,其他多數瀏覽器已經將 WindowDocumentDocument.body 在 wheel、mousewheel、touchstart 和 touchmove 事件監聽中 passive 選項預設為 true

那 scroll 事件呢?這個事件感覺也很適合使用 passive

The basic scroll event cannot be canceled, so it does not need to be set passive.

scroll 事件的預設行為本身是不能取消的,所以不需要對這類事件監聽器啟用 passive 選項。

想知道 .passive 修飾符的作用可以看以下資料:

參考資料


上一篇
真的好想離開 Vue 3 新手村 - Day 12: 認識 nextTick 與 DOM 響應更新時機 feat. template ref
下一篇
真的好想離開 Vue 3 新手村 - Day 14: style scoped 原理與特殊選擇器 :deep()&:slotted()
系列文
真的好想離開 Vue 3 新手村 feat. CompositionAPI31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言