最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue3.0中一些好用的指令封裝總結(jié)

 更新時間:2026年04月01日 09:29:03   作者:嘗嘗你的優(yōu)樂美  
Vue指令是Vue.js框架的核心特性之一,通過在HTML標(biāo)簽上添加以v-為前綴的特殊屬性,開發(fā)者可以聲明式地將數(shù)據(jù)綁定到DOM、實現(xiàn)條件渲染、循環(huán)渲染等復(fù)雜交互邏輯,這篇文章主要介紹了Vue3.0中一些好用的指令封裝的相關(guān)資料,需要的朋友可以參考下

一. 前言

最近在維護(hù)一個老項目,發(fā)現(xiàn)項目中用了很多的自定義指令。突然發(fā)現(xiàn),自己已經(jīng)很久沒有封裝過指令了,特別是vue3.0時代以來,這個功能我好像用的越來越少了。其實以前在2.0時代的時候,封裝指令是很常見的寫法(勾起了當(dāng)年的回憶)。那現(xiàn)在既然遇到了,就封裝一些很常見的指令在日常開發(fā)中可以用到的。

二. 使用流程

2.1. 介紹

其實關(guān)于指令的使用,vue3.0的官網(wǎng)寫的已經(jīng)很仔細(xì)了,對新手很友好這里是地址。寫法主要還是在提供在不同鉤子函數(shù)中,執(zhí)行不同操作。

下面 這些是官網(wǎng)給出的鉤子函數(shù)的執(zhí)行時機

const myDirective = {
  // 在綁定元素的 attribute 前
  // 或事件監(jiān)聽器應(yīng)用前調(diào)用
  created(el, binding, vnode) {
    // 這個created可以拿到dom、但是binding不會檢測到響應(yīng)式數(shù)據(jù)的改變
  },
  // 在元素被插入到 DOM 前調(diào)用
  beforeMount(el, binding, vnode) {},
  // 在綁定元素的父組件
  // 及他自己的所有子節(jié)點都掛載完成后調(diào)用
  mounted(el, binding, vnode) {},
  // 綁定元素的父組件更新前調(diào)用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在綁定元素的父組件
  // 及他自己的所有子節(jié)點都更新后調(diào)用
  updated(el, binding, vnode, prevVnode) {},
  // 綁定元素的父組件卸載前調(diào)用
  beforeUnmount(el, binding, vnode) {},
  // 綁定元素的父組件卸載后調(diào)用
  unmounted(el, binding, vnode) {}
}

這里要注意幾個參數(shù):

  • el: 綁定到指令的dom元素。
  • binding: 一個對象,包含以下屬性。
    • value: 傳遞給指令的值。(v-directive="[1,2,3]",這里的[1,2,3]就是值)
    • oldValue:之前的值。僅在 beforeUpdate 和 updated 中可用
    • arg: 傳遞給指令的參數(shù)。(v-directive:arg="", 這個arg就是參數(shù))
    • modifiers: 指令修飾符。(v-directive.foo="" 這里的foo就是修飾符)

這里就是要區(qū)分一下argmodifiers的區(qū)別。舉個例子:

v-directive:arg.foo="1"

這里的話,value就是1、 foo就是指令修飾符, arg就是指令參數(shù)。

這里要注意點,指令的參數(shù)也可以是動態(tài)的:

v-directive:[arg].foo="1"

這時候指令的參數(shù)會基于組件的 arg 數(shù)據(jù)屬性響應(yīng)式地更新。

2.2. 簡單實用

這里簡單實現(xiàn)一個v-if,順便把directive的結(jié)構(gòu)搭建了。

新建一個directives文件夾,里面新建一個myShow.ts

import type { Directive, DirectiveBinding } from 'vue'
const myShow: Directive = {
  updated(el: HTMLElement, binding: DirectiveBinding) {
    const { value } = binding
    if (value) {
      el.style.display = 'block'
    }
    else el.style.display = 'none'
  }
}
export default myShow

根據(jù)外部傳來的binding.value,來控制當(dāng)前元素的顯示與隱藏。要注意一下,這里放在update中執(zhí)行,因為在mounted中,不會檢測到binding.value值的改變。

然后在同級目錄下,新建一個index.ts

import type { App, Directive } from "vue"
import myShow from "./myShow"
interface IdirectiveList<T> {
  [key: string]: T
}
const directiveList: IdirectiveList<Directive> = {
  myShow
}
const installDirectives = {
  install(app: App<Element>) {
    for(let key in directiveList) {
      app.directive(key, directiveList[key])   // 通過app.directive注冊
    }
  }
}
export default installDirectives

最后在main.ts中把directives導(dǎo)入:

import installDirectives from './directives'
app.use(installDirectives)

三. 常見指令實現(xiàn)

實現(xiàn)之前要提一句話,組件是主要的構(gòu)建模塊,而組合式函數(shù)則側(cè)重于有狀態(tài)的邏輯。自定義指令主要是為了重用涉及普通元素的底層 DOM 訪問的邏輯。 真不愧是官網(wǎng)的原話,講的很有深意!我們使用指令的目的是為了復(fù)用dom元素的一些操作方式。所以我們的注意點是 dom元素?。。?/p>

3.1 圖片懶加載

圖片懶加載的實現(xiàn)流程:

  • 判斷元素是否進(jìn)入可視區(qū)域 使用IntersectionObserver觀察
  • 進(jìn)入可視區(qū)后加載圖片預(yù)定url
  • 最后移除圖片觀察器

我們簡單實現(xiàn)一下:新建一個lazyImage.ts:

import loadingImage from '../assets/loading.gif'
import errorImage from '../assets/error.jpeg'
import type { Directive, DirectiveBinding } from "vue";
const lazyImage: Directive = {
  mounted(el, binding: DirectiveBinding) {
    const {src, loading = loadingImage, error = errorImage } = binding.value
    el.src = loading   // 先個一個默認(rèn)的占位圖片
    const observer = new IntersectionObserver((entries: IntersectionObserverEntry[]) => {
      entries.forEach((entry: IntersectionObserverEntry) => {
        if (entry.isIntersecting) {
          imageLoading(src).then(() => {
            el.src = src
            observer.unobserve(el)   // 去除觀察
          }).catch(() => {
            el.src = error
          })
        }
      })
    }, {
      rootMargin: '50px'    //圖片 距離50px的時候開始加載
    })
    observer.observe(el)
    el.dataset.observer = observer
  },
  unmounted(el) {
    if (el.dataset.observer) {
      el.dataset.observer.unobserve(el)
    }
  }
}
const imageLoading = (src: string) => {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.src = src
    img.onload = resolve
    img.onerror = reject
  })
}
export default lazyImage

代碼簡單講解一下:

  • el.src = loading 剛進(jìn)入的時候給個默認(rèn)值loading。
  • IntersectionObserver 來監(jiān)控每個img圖片。當(dāng)圖片進(jìn)入可視區(qū)域(entry.isIntersecting)的時候,加載data里面的src。
  • observer.observe(el) 對當(dāng)前元素開啟觀察。
  • el.dataset.observer = observer 的意思是,把當(dāng)前的觀察者observer,放到dataset屬性中。這步操作的意思是,當(dāng)圖片加載正確的url后,移除對當(dāng)前圖片的觀察。
  • 在unmounted中卸載,放在一直觀察泄漏內(nèi)存。

然后我們更新一下index.ts文件。最后在頁面上使用一下:

<div class="text">
    <img
      width="600" height="400"
      v-for="(image, index) in arr"  
      v-lazyImage="{src: image}"
      :key="index"
    />
</div>
<script setup lang="ts">
import test from './assets/test.jpeg'
const arr = [test, test, test, test, test, test, test, test]
</script>

看一下最終的效果:

3.1 圖片懶加載

3.2 按鈕權(quán)限

這個在一些后臺管理的項目里面用到的很多。其實原理很簡單,就是根據(jù)接口返回、自定義的白名單中,來判斷你當(dāng)前按鈕的權(quán)限標(biāo)識在不在里面,如果在顯示,否則隱藏。

新建一個permission.ts文件:

import { type Directive, type DirectiveBinding } from "vue";
// 這里可以自定義或者從接口拿
const whiteBlock = ["per:page:1", "per:page:2", "per:page:3", "per:page:4"]
const permission: Directive = {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    if (!binding.value) return;
    else {
      console.log(binding.value)
      // 如果在白名單內(nèi),不做處理,不在直接隱藏。
      !whiteBlock.includes(binding.value) ? el.style.display = 'none' :  el.style.display = 'auto'
    }
  }
}
export default permission

沒什么好說的,很簡單。就是通過傳入的值來判斷在不在白名單之內(nèi)就可以。在頁面上使用一下:

    <el-button v-permission="'per:page:1'">per:page:1</el-button>
    <el-button v-permission="'per:page:2'">per:page:2</el-button>
    <el-button v-permission="'per:page:10'">per:page:10</el-button>
    <el-button v-permission="'per:page:3'">per:page:3</el-button>
let whiteBlock: string[] = [];
setTimeout(() => {
  whiteBlock = ["per:page:1", "per:page:2", "per:page:3", "per:page:4"]
}, 3000)

3.2 按鈕權(quán)限

沒什么問題,per:page:10不在白名單內(nèi),就不會顯示。

但是這里有個優(yōu)化點,需要提一下。如果你的白名單數(shù)據(jù)是從接口拿的,那不能在mounted階段處理邏輯問題。因為異步的問題,會出現(xiàn)el加載完成,但是whiteBlock還沒有獲取到。

比如我們把whiteBlock的賦值變成異步的。

let whiteBlock: string[] = [];
setTimeout(() => {
  whiteBlock = ["per:page:1", "per:page:2", "per:page:3", "per:page:4"]
}, 500)

這時候頁面就會變成空白。

3.2 按鈕權(quán)限_圖2

所以,這里需要對邏輯改造一下,把whiteBlock變?yōu)轫憫?yīng)式數(shù)據(jù),然后通過watchEffect監(jiān)聽一下whiteBlock。代碼如下:

import { type Directive, type DirectiveBinding } from "vue";
import { ref, watchEffect } from "vue"
// 這里可以自定義或者從接口拿
let whiteBlock = ref<string[]>([]);
setTimeout(() => {
  whiteBlock.value = ["per:page:1", "per:page:2", "per:page:3", "per:page:4"]
}, 500)
const permission: Directive = {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    watchEffect(() => {
      if (whiteBlock.value.length === 0 || !binding.value) return
      !whiteBlock.value.includes(binding.value) ? el.style.display = 'none' :  el.style.display = 'auto'
    })
  }
}
export default permission

ok! 最后要注意一下,whiteBlock的獲取一般都放在登陸的時候和用戶信息一起返回,不然的話會出現(xiàn),按鈕閃爍消失的問題。

3.3 元素的拖拽

這個其實已經(jīng)有很好用的庫了,但是不妨礙我們在實現(xiàn)一下。新建一個drag.ts文件:

import type { Directive } from "vue";
const drag: Directive = {
  mounted(el: HTMLElement) {
    el.style.cursor = 'move'
    el.onmousedown = (e: MouseEvent) => {
      const disX: number = e.clientX - el.offsetLeft;
      const disY: number = e.clientY - el.offsetTop;
      document.onmousemove = (e: MouseEvent) => {
        el.style.left = `${e.clientX - disX}px`;
        el.style.top = `${e.clientY - disY}px`;
      };
      document.onmouseup = () => {
        document.onmousemove = null;
      };
    }
  },
}
export default dra

然后在項目中使用一下:

<el-button v-drag style="position: absolute;">拖拽</el-button>

這里注意一下,我這種寫法是需要當(dāng)前文件定位為absolute的。

3.4 無限滾動

這個的實現(xiàn)原理,主要是判斷scrollHeightscrollTopclientHeight的關(guān)系。從網(wǎng)上找了一張圖:

3.4 無限滾動

如果滾動軸到達(dá) 視口底部 則執(zhí)行callback函數(shù)。所以要判斷一下: scrollHeight - scrollTop <= clientHeight

新建一個infiniteScroll.ts文件:

import type { Directive, DirectiveBinding } from "vue";
const infiniteScroll: Directive = {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    const { arg = 0, value } = binding
    const bottomDistance: number = el.clientHeight - Number(arg)
    if (el.scrollHeight - el.scrollTop <= bottomDistance) {
      value()   // 執(zhí)行回調(diào)
    }
  },
}
export default infiniteScroll

在項目中使用一下:

<div v-infiniteScroll:[distance]="callBack"> </div>

四. 總結(jié)

好用的一些指令還有很多,比如防抖、節(jié)流等。這里就不多說了。這里主要是提供一些好用的指令實現(xiàn)的方法。

到此這篇關(guān)于Vue3.0中一些好用的指令封裝的文章就介紹到這了,更多相關(guān)Vue3.0指令封裝內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue2中使用tailwindCss的詳細(xì)教程

    Vue2中使用tailwindCss的詳細(xì)教程

    Tailwind CSS是一個流行的前端CSS框架,它基于原子設(shè)計原則,提供了一套預(yù)構(gòu)建的CSS樣式類,旨在幫助開發(fā)者快速地創(chuàng)建響應(yīng)式、可定制的用戶界面,本文給大家介紹了Vue2中使用tailwindCss的詳細(xì)教程,需要的朋友可以參考下
    2024-09-09
  • vue.js實現(xiàn)標(biāo)簽頁切換效果

    vue.js實現(xiàn)標(biāo)簽頁切換效果

    這篇文章主要介紹了vue.js實現(xiàn)標(biāo)簽頁切換效果,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-06-06
  • Vue實現(xiàn)選擇城市功能

    Vue實現(xiàn)選擇城市功能

    這篇文章主要介紹了Vue實現(xiàn)選擇城市功能,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-05-05
  • Vue+Element+Springboot圖片上傳的實現(xiàn)示例

    Vue+Element+Springboot圖片上傳的實現(xiàn)示例

    最近在學(xué)習(xí)前段后分離,本文介紹了Vue+Element+Springboot圖片上傳的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2021-11-11
  • 深度解析Vue3實現(xiàn)日歷組件的五大主流方案

    深度解析Vue3實現(xiàn)日歷組件的五大主流方案

    在 Vue3 項目開發(fā)中,日歷組件是日程管理,預(yù)約系統(tǒng),數(shù)據(jù)可視化等場景的核心組件,本文將從易用性,擴展性,性能三個維度,深入分析 5 款主流 Vue3 日歷組件,并提供選型建議,幫助開發(fā)者快速找到適配場景的最佳方案
    2026-04-04
  • 解決VUE-npm ERR! C:\rj\node-v14.4.0-win-x64\nod問題

    解決VUE-npm ERR! C:\rj\node-v14.4.0-win-x64\nod問題

    這篇文章主要介紹了解決VUE-npm ERR! C:\rj\node-v14.4.0-win-x64\nod問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • vue實現(xiàn)在線進(jìn)制轉(zhuǎn)換功能

    vue實現(xiàn)在線進(jìn)制轉(zhuǎn)換功能

    這篇文章主要介紹了vue實現(xiàn)在線進(jìn)制轉(zhuǎn)換功能,本文通過實例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2025-04-04
  • Vue $router.push打開新窗口的實現(xiàn)方法

    Vue $router.push打開新窗口的實現(xiàn)方法

    在Vue中,$router.push方法默認(rèn)不支持在新窗口中打開頁面,但通過結(jié)合window.open方法和$router.resolve方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-09-09
  • vue?父子組件共用mixins的注意點

    vue?父子組件共用mixins的注意點

    這篇文章主要介紹了vue?父子組件共用mixins的注意點,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • 基于vue通用表單解決方案的思考與分析

    基于vue通用表單解決方案的思考與分析

    這篇文章主要給大家介紹了基于vue通用表單解決方案的思考與分析,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用vue具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03

最新評論

民乐县| 游戏| 托里县| 固阳县| 凤山市| 郧西县| 南城县| 全椒县| 彰化市| 定陶县| 虞城县| 东乌珠穆沁旗| 玛多县| 天台县| 澄城县| 五大连池市| 金山区| 奎屯市| 湄潭县| 胶南市| 汾西县| 安吉县| 台中县| 通渭县| 普定县| 灵丘县| 辽中县| 开鲁县| 奈曼旗| 娄烦县| 陇西县| 奉节县| 蕲春县| 宁国市| 五台县| 香格里拉县| 宁夏| 清丰县| 桐庐县| 富蕴县| 南雄市|