首頁 > 軟體

超實用:Vue 自定義指令合集

2021-05-22 20:30:07

「來源: |Vue中文社群 ID:vue_fe」

作者:Huup_We

juejin.cn/post/6963840401899782175

在 Vue2.0 中,程式碼複用和抽象的主要形式是元件。然而,有的情況下,你仍然需要對普通 DOM 元素進行底層操作,這時候就會用到自定義指令。

你可以將一些 css 樣式抽象到指令中,也可以將一些 js 操作放到指令中去執行。就使用上來說,指令不用像元件一樣需要引入和註冊,註冊後使用非常簡潔方便。

對於在項目中常用到的指令,在此做了一個合集介紹,附源碼可以直接在項目中使用。

元素點選範圍擴展指令 v-expandClick

使用該指令可以隱式的擴展元素的點選範圍,由於借用偽元素實現,故不會影響元素在頁面上的排列布局。

可傳入的參數為:上右下左擴展的範圍,單位 px,預設向外擴展 10px。指令的程式碼如下:

exportdefaultfunction (el, binding) {const s = document.styleSheets[document.styleSheets.length - 1]const DEFAULT = -10// 預設向外擴展10pxconst ruleStr = `content:"";position:absolute;top:-${top || DEFAULT}px;bottom:-${bottom || DEFAULT}px;right:-${right || DEFAULT}px;left:-${left || DEFAULT}px;`const [top, right, bottom, left] = binding.expression && binding.expression.split(',') || []const classNameList = el.className.split(' ') el.className = classNameList.includes('expand_click_range') ? classNameList.join(' ') : [...classNameList, 'expand_click_range'].join(' ') el.style.position = el.style.position || "relative"if (s.insertRule) { s.insertRule('.expand_click_range::before' + '{' + ruleStr + '}', s.cssRules.length) } else { /* IE */ s.addRule('.expand_click_range::before', ruleStr, -1) }}複製程式碼參數 Attributes:

然後你可以在模板中任何元素上使用新的 v-expandClickproperty,如下:

<div v-expandClick="20,30,40,50" @click="glabClickoutside"> 點選範圍擴大</div>複製程式碼文字內容複製指令 v-copy

使用該指令可以複製元素的文字內容(指令支援單擊複製 v-copy、雙擊複製 v-copy.dblclick、點選icon複製 v-copy.icon 三種模式),不傳參數時,預設使用單擊複製。

指令的程式碼如下:

exportdefault { bind (el, binding) {// 雙擊觸發複製if (binding.modifiers.dblclick) { el.addEventListener('dblclick', () => handleClick(el.innerText)) el.style.cursor = 'copy' }// 點選icon觸發複製elseif (binding.modifiers.icon) {if (el.hasIcon) returnconst iconElement = document.createElement('i') iconElement.setAttribute('class', 'el-icon-document-copy') iconElement.setAttribute('style', 'margin-left:5px') el.appendChild(iconElement) el.hasIcon = true iconElement.addEventListener('click', () => handleClick(el.innerText)) iconElement.style.cursor = 'copy' }// 單擊觸發複製else { el.addEventListener('click', () => handleClick(el.innerText)) el.style.cursor = 'copy' } }}functionhandleClick (text) {// 創建元素if (!document.getElementById('copyTarget')) {const copyTarget = document.createElement('input') copyTarget.setAttribute('style', 'position:fixed;top:0;left:0;opacity:0;z-index:-1000;') copyTarget.setAttribute('id', 'copyTarget')document.body.appendChild(copyTarget) }// 複製內容const input = document.getElementById('copyTarget') input.value = text input.select()document.execCommand('copy')// alert('複製成功')}複製程式碼參數 Attributes:

然後你可以在模板中任何元素上使用新的 v-copyproperty,如下:

<div v-copy> 單擊複製 </div><div v-copy.dblclick> 雙擊複製 </div><div v-copy.icon> icon複製 </div>複製程式碼元素全屏指令 v-screenfull

全屏指令,點選元素進行全屏/退出全屏的操作。支援元素後面是否插入 element-ui 的全屏圖示 el-icon-full-screen。

指令的程式碼如下:

import screenfull from'screenfull'exportdefault { bind (el, binding) {if (binding.modifiers.icon) {if (el.hasIcon) return// 創建全屏圖示const iconElement = document.createElement('i') iconElement.setAttribute('class', 'el-icon-full-screen') iconElement.setAttribute('style', 'margin-left:5px') el.appendChild(iconElement) el.hasIcon = true } el.style.cursor = el.style.cursor || 'pointer'// 監聽點選全屏事件 el.addEventListener('click', () => handleClick()) }}functionhandleClick () {if (!screenfull.isEnabled) { alert('瀏覽器不支援全屏')return } screenfull.toggle()}複製程式碼參數 Attributes:

然後你可以在模板中任何元素上使用新的 v-screenfullproperty,如下:

<div v-screenfull.icon> 全屏 </div>複製程式碼元素說明指令 v-tooltip

為元素新增說明,如同 element-ui 的 el-tooltip(問號 icon 在滑鼠覆蓋後,展示說明文字)。

指令的程式碼如下:

import Vue from'vue'exportdefaultfunction (el, binding) {if (el.hasIcon) returnconst iconElement = structureIcon(binding.arg, binding.value) el.appendChild(iconElement) el.hasIcon = true}functionstructureIcon (content, attrs) {// 拼接繫結屬性let attrStr = ''for (let key in attrs) { attrStr += `${key}=${attrs[key]} ` }const a = `<el-tooltip content=${content}${attrStr}><i class="el-icon-question" style="margin:0 10px"></i></el-tooltip>`// 創建構造器const tooltip = Vue.extend({ template: a })// 創建一個 tooltip 例項並返回 dom 節點const component = new tooltip().$mount()return component.$el}複製程式碼參數 Attributes:

然後你可以在模板中任何元素上使用新的 v-tooltipproperty,如下:

<div v-tooltip:content='tootipParams'> 提示 </div>複製程式碼舉例:

<div v-tooltip:提示內容為XXX1> 提示1</div><div v-tooltip:提示內容為XXX='tootipParams'> 提示2 </div>複製程式碼為指令傳入 element-ui 支援的參數:

data() {return { tootipParams: { placement: 'top', effect: 'light', } }}複製程式碼文字超出省略指令 v-ellipsis

使用該指令當文字內容超出寬度(預設 100 px)時自動變為省略形式。等同於使用 css:

width: 100px;whiteSpace: nowrapoverflow: hidden;textOverflow: ellipsis;複製程式碼使用指令效果:

指令的程式碼如下:

exportdefaultfunction (el, binding) { el.style.width = binding.arg || 100 + 'px' el.style.whiteSpace = 'nowrap' el.style.overflow = 'hidden'; el.style.textOverflow = 'ellipsis';}複製程式碼參數 Attributes:

然後你可以在模板中任何元素上使用新的 v-ellipsisproperty,如下:

<div v-ellipsis:100> 需要省略的文字是阿薩的副本阿薩的副本阿薩的副本阿薩的副本</div>複製程式碼回到頂部指令 v-backtop

使用該指令可以讓頁面或指定元素回到頂部。

可選指定元素,如果不指定則全局頁面回到頂部。可選在元素偏移多少 px 後顯示 backtop 元素,例如在滾動 400px 後顯示回到頂部按鈕。

指令的程式碼如下:

exportdefault { bind (el, binding, vnode) {// 響應點選後滾動到元素頂部 el.addEventListener('click', () => {const target = binding.arg ? document.getElementById(binding.arg) : window target.scrollTo({ top: 0, behavior: 'smooth' }) }) }, update (el, binding, vnode) {// 滾動到達參數值才出現繫結指令的元素const target = binding.arg ? document.getElementById(binding.arg) : windowif (binding.value) { target.addEventListener('scroll', (e) => {if (e.srcElement.scrollTop > binding.value) { el.style.visibility = 'unset' } else { el.style.visibility = 'hidden' } }) }// 判斷初始化狀態if (target.scrollTop < binding.value) { el.style.visibility = 'hidden' } }, unbind (el) {const target = binding.arg ? document.getElementById(binding.arg) : window target.removeEventListener('scroll') el.removeEventListener('click') }}複製程式碼參數 Attributes:

然後你可以在模板中任何元素上使用新的 v-backtopproperty,如下表示在 id 為 app 的元素滾動 400px 後顯示繫結指令的元素:

<div v-backtop:app="400"> 回到頂部 </div>複製程式碼也可以這樣使用,表示為一直顯示繫結指令的元素,並且是全局頁面回到頂部:

<div v-backtop> 回到頂部 </div>複製程式碼空狀態指令 v-empty

使用該指令可以顯示預設的空狀態。可以傳入預設圖片(可選,預設無圖片)、預設文字內容(可選,預設為暫無資料)、以及標示是否顯示空狀態(必選)。

指令的程式碼如下:

import Vue from"vue";exportdefault { update (el, binding, vnode) { el.style.position = el.style.position || 'relative'const { offsetHeight, offsetWidth } = elconst { visible, content, img } = binding.valueconst image = img ? `<img src="${img}" height="30%" width="30%"></img>` : ''const defaultStyle = "position:absolute;top:0;left:0;z-index:9999;background:#fff;display:flex;justify-content: center;align-items: center;"const empty = Vue.extend({ template: `<div style="height:${offsetHeight}px;width:${offsetWidth}px;${defaultStyle}"> <div style="text-align:center"> <div>${image}</div> <div>${content || '暫無資料'}</div> </div> </div>` })const component = new empty().$mount().$elif (visible) { el.appendChild(component) } else { el.removeChild(el.lastChild) } },}複製程式碼參數 Attributes:

然後你可以在模板中任何元素上使用新的 v-emptyproperty,如下傳入物件 emptyValue:

<div style="height:500px;width:500px"v-empty="emptyValue"> 原本內容複製程式碼需要傳入一個參數物件,例如顯示文字為:暫無列表,圖片路徑為 ../../assets/images/blue_big.png,控制標示 visible:

emptyValue = { content: '暫無列表', img: require('../../assets/images/blue_big.png'), visible: true,},複製程式碼徽標指令 v-badge

使用該指令在元素右上角顯示徽標。

支援配置徽標的背景顏色、徽標形狀;支援傳入徽標上顯示的數字。

指令的程式碼如下:

import Vue from'vue'const SUCCESS = '#72c140'const ERROR = '#ed5b56'const WARNING = '#f0af41'const INFO = '#4091f7'const HEIGHT = 20let flag = falseexportdefault { update (el, binding, vnode) {const { modifiers, value } = bindingconst modifiersKey = Object.keys(modifiers)let isDot = modifiersKey.includes('dot')let backgroundColor = ''if (modifiersKey.includes('success')) { backgroundColor = SUCCESS } elseif (modifiersKey.includes('warning')) { backgroundColor = WARNING } elseif (modifiersKey.includes('info')) { backgroundColor = INFO } else { backgroundColor = ERROR }const targetTemplate = isDot ? `<div style="position:absolute;top:-5px;right:-5px;height:10px;width:10px;border-radius:50%;background:${backgroundColor}"></div>` : `<div style="background:${backgroundColor};position:absolute;top:-${HEIGHT / 2}px;right:-${HEIGHT /2}px;height:${HEIGHT}px;min-width:${HEIGHT}px;border-radius:${HEIGHT / 2}px;text-align:center;line-height:${HEIGHT}px;color:#fff;padding:0 5px;">${value}</div>` el.style.position = el.style.position || 'relative' const badge = Vue.extend({ template: targetTemplate }) const component = new badge().$mount().$el if (flag) { el.removeChild(el.lastChild) } el.appendChild(component) flag = true }}複製程式碼參數 Attributes:

然後你可以在模板中任何元素上使用新的 v-badgeproperty,如下:

<div v-badge.dot.info="badgeCount"style="height:50px;width:50px;background:#999"></div>複製程式碼拖拽指令 v-drag

使用該指令可以對元素進行拖拽。

指令的程式碼如下:

exportdefault {let _el = eldocument.onselectstart = function() {returnfalse//禁止選擇網頁上的文字 } _el.onmousedown = e => {let disX = e.clientX - _el.offsetLeft //滑鼠按下,計算當前元素距離可視區的距離let disY = e.clientY - _el.offsetTopdocument.onmousemove = function(e){ let l = e.clientX - disXlet t = e.clientY - disY; _el.style.left = l + "px" _el.style.top = t + "px" }document.onmouseup = e => {document.onmousemove = document.onmouseup = null }returnfalse }}複製程式碼然後你可以在模板中任何元素上使用新的 v-dragproperty,如下:

<div v-drag> 支援拖拽的元素 </div>複製程式碼響應縮放指令 v-resize

使用該指令可以響應元素寬高改變時執行的方法。

指令的程式碼如下:

exportdefault {bind(el, binding) {let width = '', height = '';functionisReize() {const style = document.defaultView.getComputedStyle(el);if (width !== style.width || height !== style.height) { binding.value(); // 執行傳入的方法 } width = style.width; height = style.height; } el.__timer__ = setInterval(isReize, 300); // 週期性監聽元素是否改變 },unbind(el) {clearInterval(el.__timer__); }}複製程式碼參數 Attributes:

然後你可以在模板中任何元素上使用新的 v-resizeproperty,如下:

// 傳入 resize() 方法<div v-resize="resize"></div>複製程式碼如何使用這些指令?

為了便於管理指令,我們將每個指令都存在於單獨的 js 檔案中,你可以像這樣將指令 import 進來後註冊指令:

import Vue from'vue'import ellipsis from'./ellipsis'// 引入指令// import other directivesconst directives = { ellipsis// other directives}Object.keys(directives).forEach(name => Vue.directive(name, directives[name]))複製程式碼或者你可以直接使用 Vue.deirect 進行註冊:

// 註冊一個全局自定義指令 `v-focus`Vue.directive('focus', {// 當被繫結的元素插入到 DOM 中時…… inserted: function (el) {// 聚焦元素 el.focus() }})複製程式碼這樣就可以正常使用這些指令了:

<div v-指令名稱 />複製程式碼總結

我們常常在引入全局功能時,主要都是寫於 js 檔案、元件中。不同於他們在使用時每次需要引用或註冊,在使用上指令更加簡潔。

除了將功能封裝成元件,還可以多多考慮將一些簡潔實用的功能放到 deirect 中。例如:常用的 css 樣式、js 的一些操作、utils 中的一些工具方法、甚至是一個完整的元件都可以放進去(不過需要考慮一下效能和複雜度)。


IT145.com E-mail:sddin#qq.com