iT邦幫忙

2024 iThome 鐵人賽

DAY 25
1
Modern Web

Vue 3 初學者:用實作帶你看過核心概念系列 第 25

Vue 3 用實作帶你看過核心概念 - Day 25:自定義指令(Custom Directives)

  • 分享至 

  • xImage
  •  

文章背景圖

目錄

  • v-focus 自定義指令特色
  • 自定義指令實作方式:全域註冊與區域註冊
  • v-localStorage - 自定義指令表單緩存
  • 結論
  • 小試身手

Vue不僅提供了豐富的內置指令用於操作DOM元素,還允許開發者創建自定義指令。透過自定義指令,我們可以對DOM元素進行更細緻的控制,並在指令的不同生命週期階段執行特定的邏輯,從而實現更靈活的功能擴展。

【 指令常用的生命週期 】

  • mounted(el, binding, vnode) {}:當綁定元素的父組件及其子組件全部掛載完成後調用。
  • updated(el, binding, vnode, prevVnode) {}:當綁定元素的父組件及其子組件更新完成後調用。
  • unmounted(el, binding, vnode) {}:當綁定元素的父組件及其子組件卸載完成後調用。

⭐ 自定義指令應主要用於直接操作DOM的場景。對於其他需求,優先考慮使用內置指令(如v-bind)進行聲明式的模板處理,這樣能提升代碼的可讀性與效率。

v-focus 自定義指令特色

這裡透過實作自定義指令v-focus,說明使用自定義指令來控制DOM元素相比於其他方式(如原生autofocus屬性和ref)的優勢。自定義指令可以更靈活地控制元素的焦點行為,並且在元素插入或更新時都能確保獲取焦點。

【 v-focus 自定義指令實做方式 】

👉 Vue3 Options API 自定義指令區域註冊實作連結

Vue Template:

<div id="app">
  <button @click="changeButtonVisible" type="button">toggle</button>
  <div v-if="isVisible" class="input-group">
    <label for="text-input">我是輸入框:</label>
    <input v-focus id="text-input" type="text">
  </div>
</div>

javascript:

const focus = {
  mounted: (el) => el.focus()
};

const rootComponent = {
  data() {
    return {
      isVisible: false
    };
  },
  methods: {
    changeButtonVisible() {
      this.isVisible = !this.isVisible;
    }
  },
  directives: { focus }
};

【 ref 實做修改方式 】

👉 Vue3 Options API ref mounted focus實作連結

Vue Template:

 <input v-if="isVisible" id="text-input" ref="inputVal" type="text">

javascript:

mounted(){
 this.$refs.inputVal.focus()
}

【 原生 HTML autofocus 屬性 】

Vue Template:

 <input v-if="isVisible" id="text-input" type="text" autofocus>

無論是使用ref來手動設置焦點,還是使用原生 HTML autofocus屬性,這些方法都相對較難精確地處理元素在創建和銷毀時的焦點控制問題,通常只能在初始化時賦予輸入框焦點,但在點擊toggle後,無法保持持續的焦點效果。而自定義指令可以更自然地與元素的生命週期鉤子結合,從而在元素插入或移除時進行更靈活的操作。

自定義指令實作方式:全域註冊與區域註冊

自定義指令可以透過區域註冊或全域註冊來使用,根據不同的應用場景,我們可以選擇合適的註冊方式:

  • 區域註冊指令:僅在某個組件中可用,適合僅對某個組件應用的特定邏輯。
  • 全域註冊指令:在整個應用中可用,適合所有組件中都可能需要的功能。

以上面v-focus自定義指令案例說明不同的註冊方式:

👉 Vue3 Options API ref mounted focus實作連結

javascript - 區域註冊指令(v-focus)

const focus = {
  mounted: (el) => el.focus()
};

const rootComponent = {
  directives: { focus }
};

javascript - 全域註冊指令(v-select)

const app = createApp(rootComponent);

app.directive("select", {
  mounted: (el) => el.select()
});

v-localStorage - 自定義指令表單緩存

在自定義指令中,使用不同的Hook時可以訪問一些常見的參數:

  • el:指令綁定的 DOM 元素。
  • binding:包含與指令綁定相關的信息。
    • value:指令所綁定的值,例如:v-focus="value" 中的 value。
    • arg:指令的參數,例如:v-focus:arg 中的 arg。
    • oldValue:指令之前的綁定值,僅在beforeUpdateupdated hook 中可用。無論值是否更改,oldValue 都可用。
    • modifiers:包含修飾符的對象,例如:v-focus.lazy 中的 lazy 修飾符。
    • instance:綁定指令的組件實例,可以透過它訪問響應式數據,例如 binding.instance.xxx。

⭐ 除了el參數之外其餘參數都是唯獨屬性。如果要跨不同 Hook 傳值使用,官方推薦使用dataset attribute的方式。

以下是一個範例說明如何使用自定義指令來暫存表單數據至localStorage,並結合前述的參數使用方式:

👉 Vue3 Options API 表單自定義指令 v-localStorage 實作連結

流程說明:

  1. 當表單裡面的輸入框輸入值且失去焦點的時候觸發緩存
  2. 將緩存的值儲存進去localStorage進行緩存
  3. 若使用者重新整理且處在未送出的狀態之下,可以藉由localStorage顯示未送出前的內容

Vue Template:

<form v-localstorage:UserInfo @submit.prevent>
    <div class="input-group">
      <label for="username">用戶名:</label>
      <input type="text" id="username" name="username" />
    </div>
    <div class="input-group">
      <label for="phone">電話號碼:</label>
      <input type="tel" id="phone" name="phone" />
    </div>
    <button type="submit">提交</button>
</form>

javascript(簡化版本):

const localStorage = {
  mounted(el, binding) {
    // 取得 arg 的參數
    const key = binding.arg || "formData";
    const savedData = JSON.parse(window.localStorage.getItem(key)) || {};
      populateForm(el, savedData);
      
    function handler() {
      const formData = getFormData(el);
      window.localStorage.setItem(key, JSON.stringify(formData));
        
      // 同步更新 Vue 中的響應式數據
      if (binding.instance && binding.instance.storageData) {
        binding.instance.storageData = formData;
      }
    }

    el.addEventListener("change", handler);
    // 將 handler 保存到元素上,以便後續移除
    el._localStorage_handler = handler;
  },

  unmounted(el) {
    // 如果存在處理函數,則移除事件監聽器
    if (el._localStorage_handler) {
      el.removeEventListener("change", el._localStorage_handler);
      delete el._localStorage_handler;
    }
  }
};

結論

自定義指令為Vue開發提供了一種靈活的方式來封裝對DOM元素的操作,允許在元素的不同生命週期鉤子中進行控制,以實現特定功能需求。然而需要注意的是,自定義指令無法像屬性透傳那樣被深層組件使用,因此不建議直接應用於組件,避免多個根節點帶來的潛在問題。

小試身手

這次提供一個結合Bootstrap樣式的輸入框,並通過 Vue.js 自定義指令v-validate來實現即時的驗證。指令會在updated生命週期中取得用戶的輸入值進行驗證,根據結果應用對應的Bootstrap樣式,顯示輸入的有效或無效狀態。

Vue3 Options API 自定義指令 v-validate - 電子信箱驗證(模板)
Vue3 Options API 自定義指令 v-validate - 電子信箱驗證(完成版)


上一篇
Vue 3 用實作帶你看過核心概念 - Day 24:異步加載(Lazy loading)
下一篇
Vue 3 用實作帶你看過核心概念 - Day 26:KeepAlive 與 Teleport 內置組件
系列文
Vue 3 初學者:用實作帶你看過核心概念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言