亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在Vue3中實現自定義指令

發布時間:2022-06-13 11:48:15 來源:億速云 閱讀:206 作者:zzz 欄目:開發技術

這篇文章主要介紹“怎么在Vue3中實現自定義指令”,在日常操作中,相信很多人在怎么在Vue3中實現自定義指令問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么在Vue3中實現自定義指令”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

前言

我們需要明白為什么需要自定義一個指令,其實就是想更加簡潔地重復使用操作DOM的邏輯,這就和組件化和組合式函數差不多。

不管是Vue內置的指令還是自定義的指令,都有類似于組件的生命周期,我們可以在不同的生命周期完成不同的邏輯操作,并綁定到組件元素上,這樣就產生了自定義指令。在Vue3中,我們有三種方式可以定義指令:

  • 如果是在<script setup>定義組件內的指令,有一個語法糖可以使用:任何以v開頭的駝峰式命名的變量都可以被用作一個自定義指令,然后在模板中使用。舉一個簡單的例子:在輸入框渲染后自動聚焦

    <script setup>
    // 在模板中啟用 v-focus
    const vFocus = {
      mounted: (el) => el.focus()
    }
    </script>
    
    <template>
      <input v-focus />
    </template>

    運行效果:

怎么在Vue3中實現自定義指令

  • 如果是使用選項式,則自定義指令需要在directives選項中注冊。同上一個例子:

    <script>
    export default{
      setup() {},
      directives: {
        // 指令名
        focus: {
          // 生命周期
          mounted(el) {
            // 處理DOM的邏輯
            el.focus();
          },
        }
      }
    }
    </script>
    <template>
      <input v-focus />
    </template>

    實現的效果也是和上一個例子一樣。

  • 除了注冊組件內指令,我們還可以自定義全局指令,這樣在所有的組件中都可以使用該指令

    // main.js
    import { createApp } from 'vue'
    import App from './App.vue'
    
    const app = createApp(App)
    app.directive('focus', {
      mounted(el) {
        el.focus();
      }
    })
    app.mount('#app')

    實現效果也是一樣的。

這三種方式我們選擇最后一種,其他兩種方式可以按照類似的方式實現。

生命周期

指令的生命周期和組件的生命周期類似:

app.directive('focus', {
  created() {
    console.log('created');
  },
  beforeMount() {
    console.log('beforeMount');
  },
  mounted() {
    console.log('mounted');
  },
  beforeUpdate() {
    console.log('beforeUpdate');
  },
  updated() {
    console.log('updated');
  },
  beforeUnmount() {
    console.log('beforeUnmount');
  },
  unmounted() {
    console.log('unmounted');
  }
})

運行結果:

怎么在Vue3中實現自定義指令

注意指令沒有beforeCreated鉤子。

  • created:在綁定元素的屬性前,或者事件監聽器應用前調用

  • beforeMount:在元素被插入到DOM前調用,例如我們想要實現輸入框的自動聚焦,就不能在beforeMount鉤子中實現

  • mounted:在綁定元素的父組件以及自己的所有子節點都掛載完畢后調用,這個時候DOM已經渲染出來,我們實現輸入框自動聚焦也是在這個鉤子函數中實現

  • beforeUpdate:綁定元素的父組件更新前調用

  • updated:在綁定元素的父組件以及自己的所有子節點都更新完畢后調用

  • beforeUnmount:綁定元素的父組件卸載前調用

  • unmounted:綁定元素的父組件卸載后調用

每個鉤子函數都有對應的參數,接下來繼續看鉤子參數。

鉤子的參數

指令是為了能重用對DOM的操作邏輯,因此指令參數可以有1-4個參數,其中必需的參數就是當前綁定的DOM元素。

語法:

created(el, binding, vnode, preVnode) {}

參數比較多,我們一個一個來學習。

  • el:指令綁定到的DOM元素,可以用于直接操作當前元素,默認傳入鉤子的就是el參數,例如我們開始實現的focus指令,就是直接操作的元素DOM

  • binding:這是一個對象,包含以下屬性:

    • value:在元素上使用指令時,傳遞給指令的值。例如:<div v-reverse="'hello'"></div>,傳遞給reserve指令的值就是hello,我們可以拿到值并做相關處理

    • oldValue:之前的值,一般用于beforeUpateupdated鉤子函數中,例如:beforeUpdate(el, {oldValue: ''})

    • arg:傳遞給指令的參數,非必需,例如<div v-reverse:foo="'hello'"></div>,那么傳遞給指令的參數就是foo

    • modifiers:一個由修飾符構成的對象,例如<div v-reverse.foo.bar="'hello'"></div>,那么該修飾符對象為{foo: true, bar: true},我們經常使用到的事件修飾符,其實和這個差不多。

    • instance:使用該指令的組件實例,注意不是DOM

    • dir:指令的定義對象

  • vnode:綁定元素的地城VNode

  • preVnode:之前的渲染中代表指令所綁定的元素的VNode,一般用于beforeUpateupdated鉤子函數中

可能看這些參數會一時迷糊,我們來看一個例子:

定義一個可翻轉輸入框輸入的指令,注意鉤子函數要選擇beforeUpdate

app.directive('reserve', {
  beforeUpdate(el, binding) {
    console.log(binding);
    el.innerText = binding.value ? binding.value.split('').reverse().join('') : '';
  }
})

在模板中使用:輸入框輸入值,div會顯示反轉后的值

<script setup>
import {ref} from 'vue'
let hello = ref('')
</script>
<template>
  <input v-focus v-model="hello" />
  <div v-reserve:foo.bar="hello"></div>
</template>

運行結果:

怎么在Vue3中實現自定義指令

怎么在Vue3中實現自定義指令

結合該圖,是不是就更能理解鉤子參數的含義了。

簡化形式

我們在寫指令的時候,可以具體指定在哪些鉤子中執行一些邏輯。有時候指令的鉤子不止一個,但是又是重復的邏輯操作時,重復寫一遍代碼顯然有點不夠優雅。在Vue中,如果我們在自定義指令時,需要在mountedupdated中實現相同的行為,并且不關心其他鉤子的情況,那么我們開可以采用簡寫:

app.directive('color', (el, binding) => {
    // 這將會在mounted和updated時調用
    el.style.color = binding.value;
})

對象字面量

我們之前的例子中,傳遞給指令的值只有一個,如果我們想給指令傳入多個值應該怎么操作呢?很簡單,傳入一個字面量對象即可,可以直接在模板中聲明,也可以使用響應式對象,在使用時binding.value就是一個對象了,而不是一個普通的值。

<script setup>
import {ref, reactive} from 'vue'
let hello = ref('')
const obj = reactive({
  hello: '',
  world: ''
})
</script>
<template>
  <input v-focus v-model="obj.hello" />
  <div v-reserve:foo.bar="obj"></div>
  <!-- <div v-reserve:foo.bar="{hello: obj.hello, world: obj.world}"></div> -->
</template>

對應的,我們的指令也要小小的修改一下:

el.innerText = binding.value ? binding.value.hello.split('').reverse().join('') : '';

實現的效果還是和上面的保持一致。

在組件上使用指令

在元素上直接使用指令,我們可以在指令中操作DOM,這個已經沒有問題了。那如果在組件上使用指令會怎樣呢?組件其實就是把一些DOM元素封裝起來,Vue3和Vue2不同,Vue3的模板中可以不止一個根節點。

我們新建一個Reverse.vue,以便后續作為組件引入。

Vue2:模板中只能有一個根節點,因此會報錯

// Reverse.vue
<template>
    <div></div>
    <div></div>
</template>

Vue3:模板中可以不止一個根節點,正常

// Reverse.vue
<template>
    <div></div>
    <div></div>
</template>

既然指令是為了操作DOM元素,如果只有單個根節點那不會有問題,例如:

<script setup>
...
import ReverseVue from './Reserve.vue'
...
</script>
<template>
  ...
  <ReverseVue v-reserve="obj"/>
</template>
// Reverse.vue
<template>
    <!-- v-reserve 指令會被應用在此處 -->
    <div></div>
</template>

如果模板中是多個根節點,就會拋出警告,并且不執行指令

// Reverse.vue
<template>
    <!-- v-reserve 不會作用,并且會拋出警告 -->
    <div></div>
    <div></div>
</template>

結論:盡量不要在組件上使用自定義指令,除非能確定只會有一個根節點

幾個實用的自定義指令

以下舉例的指令都是全局指令

自動聚焦v-focus

聚焦比較特殊,兄弟元素間只會有一個聚焦,即將該指令作用于兩個兄弟輸入框上,只會自動聚焦一個

app.directive('focus', (el) => {
    el.focus();
})

防抖v-debounce

在實際項目開發中,經常會聽到服務端的同事抱怨:前端怎么不做限流呀。前端做”限流“一般會采用防抖和節流,我們先來看如何實現防抖。

步驟:

  • 首先我們得知道怎么寫一個防抖函數

  • 然后需要將防抖函數與el節點綁定,為了通用的話,還需要考慮傳入事件類型

  • 最后是卸載定時器等操作

app.directive('debounce', {
  mounted(el, binding) {
    // 至少需要回調函數以及監聽事件類型
    if (typeof binding.value.fn !== 'function' || !binding.value.event) return;
    let delay = 200; // 默認延遲時間
    el.timer = null;
    el.handler = function() {
      if (el.timer) {
        clearTimeout(el.timer);
        el.timer = null;
      };
      el.timer = setTimeout(() => {
        binding.value.fn.apply(this, arguments)
        el.timer = null;
      }, binding.value.delay || delay);
    }
    el.addEventListener(binding.value.event, el.handler)
  },
  // 元素卸載前也記得清理定時器并且移除監聽事件
  beforeMount(el, binding) {
    if (el.timer) {
      clearTimeout(el.timer);
      el.timer = null;
    }
    el.removeEventListener(binding.value.event, el.handler)
  }
})

在模板中使用:

<script setup>
const handleClick = () => {
  console.log('防抖點擊');
}
</script>
<template>
  <button v-debounce="{fn: handleClick, event: 'click', delay: 200}">點擊試試</button>
</template>

運行結果:

快速點擊按鈕并不會立即觸發handleClick,而是會在指定的延遲時間后才會觸發。

節流v-throttle

節流和防抖類似,都是用于前端”限流“。不同的是,防抖是限制執行次數,多次密集的觸發只會執行最后一次,無規律,更關注結果;節流是限制執行頻率,有節奏的執行,有規律, 更關注過程。

節流的實現和防抖差不多:

app.directive('throttle', {
  mounted(el, binding) {
    // 至少需要回調函數以及監聽事件類型
    if (typeof binding.value.fn !== 'function' || !binding.value.event) return;
    let delay = 200;
    el.timer = null;
    el.handler = function() {
      if (el.timer) return;
      el.timer = setTimeout(() => {
        binding.value.fn.apply(this, arguments)
        el.timer = null;
      }, binding.value.delay || delay);
    }
    el.addEventListener(binding.value.event, el.handler)
  },
  // 元素卸載前也記得清理定時器并且移除監聽事件
  beforeMount(el, binding) {
    if (el.timer) {
      clearTimeout(el.timer);
      el.timer = null;
    }
    el.removeEventListener(binding.value.event, el.handler)
  }
})

在模板中使用:

<script setup>
import {reactive} from 'vue'
const obj = reactive({
  hello: '',
  world: ''
})
const handleInput = () => {
  console.log('節流輸入框的值:', obj.hello);
}
</script>
<template>
  <input v-throttle="{fn: handleInput, event: 'input', delay: 1000}" v-model="obj.hello" />
</template>

運行結果:

handleInput并不會因為我在輸入框輸入時的快慢而觸發,而是在固定的時間間隔內觸發一次,這就是節流。

彈窗隱藏v-hide

在實際開發時會有這樣的需求:點擊某一個按鈕出現一個彈窗,然后點彈窗的其他區域時需要關閉彈窗,如果是點擊的彈窗本身,除非是關閉操作,否則不關閉彈窗。

想要實現這種效果,大多數人都會想到全局監聽click事件,并且判斷點擊的目標元素和我們的彈窗元素是不是同一個,如果不是那就隱藏彈窗。那么我們就來看看具體應該怎么實現:

app.directive('hide', {
  mounted(el, binding) {
    el.handler = function(e) {
      // 如果點擊范圍在綁定的元素范圍內,那么將不執行指令操作,而是執行原點擊事件
      if (el.contains(e.target)) return;
      if (typeof binding.value.fn === 'function') {
        // 綁定給指令的如果是一個函數,那么將回調并指定this
        binding.value.fn.apply(this, arguments)
        // 并不推薦使用style的方式來隱藏元素,這樣的話控制彈窗的變量就無法改變,所以推薦使用回調函數
        // el.style.display = 'none';
        // 解除事件綁定
        document.removeEventListener('click', el.handler)
      }
    }
    // 監聽全局的點擊事件
    document.addEventListener('click', el.handler)
    // 如果同步綁定全局事件不生效,可以采用異步的方式
    // setTimeout(() => {
    //   document.addEventListener('click', el.handler)
    // }, 0);
  },
  // 解除事件綁定
  beforeMount(el) {
    document.removeEventListener('click', el.handler)
  }
})

在模板中使用:

<script setup>
import {ref} from 'vue'
let isShowModal = ref(false)
const showModal = () => {
  isShowModal.value = true;
}
const cancleModal = () => {
  console.log('cancleModal');
  isShowModal.value = false;
}
</script>
<template>
  <button @click.stop="showModal">點擊顯示彈窗</button>
  <div class="modal" v-hide="{fn: cancleModal}" v-if="isShowModal">
    <p>我是彈窗</p>
    <button @click.stop="cancleModal">關閉</button>
  </div>
</template>

運行結果:

怎么在Vue3中實現自定義指令

到此,關于“怎么在Vue3中實現自定義指令”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

巩留县| 会泽县| 禄劝| 陵川县| 吴川市| 柞水县| 阿克苏市| 九龙城区| 临猗县| 托里县| 兴国县| 遂川县| 家居| 礼泉县| 义乌市| 康乐县| 洛阳市| 交口县| 林州市| 大厂| 蒙城县| 拉萨市| 本溪| 新宾| 香河县| 东乡县| 阳江市| 大英县| 饶平县| 阳新县| 沛县| 大城县| 张掖市| 陈巴尔虎旗| 连江县| 辽源市| 巴彦县| 土默特右旗| 木里| 三门峡市| 葫芦岛市|