<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          你一定用得上的 8 個非常實用的 Vue 自定義指令

          共 10725字,需瀏覽 22分鐘

           ·

          2020-12-22 22:46

          作者:lzg9527

          https://juejin.cn/post/6906028995133833230

          在 Vue,除了核心功能默認(rèn)內(nèi)置的指令 ( v-model 和 v-show ),Vue 也允許注冊自定義指令。它的作用價值在于當(dāng)開發(fā)人員在某些場景下需要對普通 DOM 元素進(jìn)行操作。

          Vue 自定義指令有全局注冊和局部注冊兩種方式。先來看看注冊全局指令的方式,通過Vue.directive( id, [definition] )?方式注冊全局指令。然后在入口文件中進(jìn)行?Vue.use()?調(diào)用。

          批量注冊指令,新建?directives/index.js?文件

          import?copy?from?'./copy'
          import?longpress?from?'./longpress'
          //?自定義指令
          const?directives?=?{
          ??copy,
          ??longpress,
          }

          export?default?{
          ??install(Vue)?{
          ????Object.keys(directives).forEach((key)?=>?{
          ??????Vue.directive(key,?directives[key])
          ????})
          ??},
          }

          在?main.js?引入并調(diào)用

          import?Vue?from?'vue'
          import?Directives?from?'./JS/directives'
          Vue.use(Directives)

          指令定義函數(shù)提供了幾個鉤子函數(shù)(可選):

          • bind: 只調(diào)用一次,指令第一次綁定到元素時調(diào)用,可以定義一個在綁定時執(zhí)行一次的初始化動作。
          • inserted: 被綁定元素插入父節(jié)點時調(diào)用(父節(jié)點存在即可調(diào)用,不必存在于 document 中)。
          • update: 被綁定元素所在的模板更新時調(diào)用,而不論綁定值是否變化。通過比較更新前后的綁定值。
          • componentUpdated: 被綁定元素所在模板完成一次更新周期時調(diào)用。
          • unbind: 只調(diào)用一次, 指令與元素解綁時調(diào)用。

          下面分享幾個實用的 Vue 自定義指令

          • 復(fù)制粘貼指令?v-copy
          • 長按指令?v-longpress
          • 輸入框防抖指令?v-debounce
          • 禁止表情及特殊字符?v-emoji
          • 圖片懶加載?v-LazyLoad
          • 權(quán)限校驗指令?v-premission
          • 實現(xiàn)頁面水印?v-waterMarker
          • 拖拽指令?v-draggable

          v-copy

          需求:實現(xiàn)一鍵復(fù)制文本內(nèi)容,用于鼠標(biāo)右鍵粘貼。

          思路:

          1. 動態(tài)創(chuàng)建?textarea?標(biāo)簽,并設(shè)置?readOnly?屬性及移出可視區(qū)域
          2. 將要復(fù)制的值賦給?textarea?標(biāo)簽的?value?屬性,并插入到?body
          3. 選中值?textarea?并復(fù)制
          4. 將?body?中插入的?textarea?移除
          5. 在第一次調(diào)用時綁定事件,在解綁時移除事件
          const?copy?=?{
          ??bind(el,?{?value?})?{
          ????el.$value?=?value
          ????el.handler?=?()?=>?{
          ??????if?(!el.$value)?{
          ????????//?值為空的時候,給出提示??筛鶕?jù)項目UI仔細(xì)設(shè)計
          ????????console.log('無復(fù)制內(nèi)容')
          ????????return
          ??????}
          ??????//?動態(tài)創(chuàng)建?textarea?標(biāo)簽
          ??????const?textarea?=?document.createElement('textarea')
          ??????//?將該?textarea?設(shè)為?readonly?防止?iOS?下自動喚起鍵盤,同時將?textarea?移出可視區(qū)域
          ??????textarea.readOnly?=?'readonly'
          ??????textarea.style.position?=?'absolute'
          ??????textarea.style.left?=?'-9999px'
          ??????//?將要?copy?的值賦給?textarea?標(biāo)簽的?value?屬性
          ??????textarea.value?=?el.$value
          ??????//?將?textarea?插入到?body?中
          ??????document.body.appendChild(textarea)
          ??????//?選中值并復(fù)制
          ??????textarea.select()
          ??????const?result?=?document.execCommand('Copy')
          ??????if?(result)?{
          ????????console.log('復(fù)制成功')?//?可根據(jù)項目UI仔細(xì)設(shè)計
          ??????}
          ??????document.body.removeChild(textarea)
          ????}
          ????//?綁定點擊事件,就是所謂的一鍵?copy?啦
          ????el.addEventListener('click',?el.handler)
          ??},
          ??//?當(dāng)傳進(jìn)來的值更新的時候觸發(fā)
          ??componentUpdated(el,?{?value?})?{
          ????el.$value?=?value
          ??},
          ??//?指令與元素解綁的時候,移除事件綁定
          ??unbind(el)?{
          ????el.removeEventListener('click',?el.handler)
          ??},
          }

          export?default?copy

          使用:給 Dom 加上?v-copy?及復(fù)制的文本即可

          <template>
          ??<button?v-copy="copyText">復(fù)制button>
          template>

          <script>
          ??export?default?{
          ????data()?{
          ??????return?{
          ????????copyText:?'a?copy?directives',
          ??????}
          ????},
          ??}
          script>

          v-longpress

          需求:實現(xiàn)長按,用戶需要按下并按住按鈕幾秒鐘,觸發(fā)相應(yīng)的事件

          思路:

          1. 創(chuàng)建一個計時器, 2 秒后執(zhí)行函數(shù)
          2. 當(dāng)用戶按下按鈕時觸發(fā)?mousedown?事件,啟動計時器;用戶松開按鈕時調(diào)用mouseout事件。
          3. 如果?mouseup?事件 2 秒內(nèi)被觸發(fā),就清除計時器,當(dāng)作一個普通的點擊事件
          4. 如果計時器沒有在 2 秒內(nèi)清除,則判定為一次長按,可以執(zhí)行關(guān)聯(lián)的函數(shù)。
          5. 在移動端要考慮?touchstarttouchend?事件
          const?longpress?=?{
          ??bind:?function?(el,?binding,?vNode)?{
          ????if?(typeof?binding.value?!==?'function')?{
          ??????throw?'callback?must?be?a?function'
          ????}
          ????//?定義變量
          ????let?pressTimer?=?null
          ????//?創(chuàng)建計時器(?2秒后執(zhí)行函數(shù)?)
          ????let?start?=?(e)?=>?{
          ??????if?(e.type?===?'click'?&&?e.button?!==?0)?{
          ????????return
          ??????}
          ??????if?(pressTimer?===?null)?{
          ????????pressTimer?=?setTimeout(()?=>?{
          ??????????handler()
          ????????},?2000)
          ??????}
          ????}
          ????//?取消計時器
          ????let?cancel?=?(e)?=>?{
          ??????if?(pressTimer?!==?null)?{
          ????????clearTimeout(pressTimer)
          ????????pressTimer?=?null
          ??????}
          ????}
          ????//?運行函數(shù)
          ????const?handler?=?(e)?=>?{
          ??????binding.value(e)
          ????}
          ????//?添加事件監(jiān)聽器
          ????el.addEventListener('mousedown',?start)
          ????el.addEventListener('touchstart',?start)
          ????//?取消計時器
          ????el.addEventListener('click',?cancel)
          ????el.addEventListener('mouseout',?cancel)
          ????el.addEventListener('touchend',?cancel)
          ????el.addEventListener('touchcancel',?cancel)
          ??},
          ??//?當(dāng)傳進(jìn)來的值更新的時候觸發(fā)
          ??componentUpdated(el,?{?value?})?{
          ????el.$value?=?value
          ??},
          ??//?指令與元素解綁的時候,移除事件綁定
          ??unbind(el)?{
          ????el.removeEventListener('click',?el.handler)
          ??},
          }

          export?default?longpress

          使用:給 Dom 加上?v-longpress?及回調(diào)函數(shù)即可

          <template>
          ??<button?v-longpress="longpress">長按button>
          template>

          <script>
          export?default?{
          ??methods:?{
          ????longpress?()?{
          ??????alert('長按指令生效')
          ????}
          ??}
          }

          v-debounce

          背景:在開發(fā)中,有些提交保存按鈕有時候會在短時間內(nèi)被點擊多次,這樣就會多次重復(fù)請求后端接口,造成數(shù)據(jù)的混亂,比如新增表單的提交按鈕,多次點擊就會新增多條重復(fù)的數(shù)據(jù)。

          需求:防止按鈕在短時間內(nèi)被多次點擊,使用防抖函數(shù)限制規(guī)定時間內(nèi)只能點擊一次。

          思路:

          1. 定義一個延遲執(zhí)行的方法,如果在延遲時間內(nèi)再調(diào)用該方法,則重新計算執(zhí)行時間。
          2. 將時間綁定在 click 方法上。
          const?debounce?=?{
          ??inserted:?function?(el,?binding)?{
          ????let?timer
          ????el.addEventListener('keyup',?()?=>?{
          ??????if?(timer)?{
          ????????clearTimeout(timer)
          ??????}
          ??????timer?=?setTimeout(()?=>?{
          ????????binding.value()
          ??????},?1000)
          ????})
          ??},
          }

          export?default?debounce

          使用:給 Dom 加上?v-debounce?及回調(diào)函數(shù)即可

          <template>
          ??<button?v-debounce="debounceClick">防抖button>
          template>

          <script>
          export?default?{
          ??methods:?{
          ????debounceClick?()?{
          ??????console.log('只觸發(fā)一次')
          ????}
          ??}
          }

          v-emoji

          背景:開發(fā)中遇到的表單輸入,往往會有對輸入內(nèi)容的限制,比如不能輸入表情和特殊字符,只能輸入數(shù)字或字母等。

          我們常規(guī)方法是在每一個表單的?on-change?事件上做處理。

          <template>
          ??<input?type="text"?v-model="note"?@change="vaidateEmoji"?/>
          template>

          <script>
          ??export?default?{
          ????methods:?{
          ??????vaidateEmoji()?{
          ????????var?reg?=?/[^\u4E00-\u9FA5|\d|\a-zA-Z|\r\n\s,.?!,。?!…—&$=()-+/*{}[\]]|\s/g
          ????????this.note?=?this.note.replace(reg,?'')
          ??????},
          ????},
          ??}
          script>

          這樣代碼量比較大而且不好維護(hù),所以我們需要自定義一個指令來解決這問題。

          需求:根據(jù)正則表達(dá)式,設(shè)計自定義處理表單輸入規(guī)則的指令,下面以禁止輸入表情和特殊字符為例。

          let?findEle?=?(parent,?type)?=>?{
          ??return?parent.tagName.toLowerCase()?===?type???parent?:?parent.querySelector(type)
          }

          const?trigger?=?(el,?type)?=>?{
          ??const?e?=?document.createEvent('HTMLEvents')
          ??e.initEvent(type,?true,?true)
          ??el.dispatchEvent(e)
          }

          const?emoji?=?{
          ??bind:?function?(el,?binding,?vnode)?{
          ????//?正則規(guī)則可根據(jù)需求自定義
          ????var?regRule?=?/[^\u4E00-\u9FA5|\d|\a-zA-Z|\r\n\s,.?!,。?!…—&$=()-+/*{}[\]]|\s/g
          ????let?$inp?=?findEle(el,?'input')
          ????el.$inp?=?$inp
          ????$inp.handle?=?function?()?{
          ??????let?val?=?$inp.value
          ??????$inp.value?=?val.replace(regRule,?'')

          ??????trigger($inp,?'input')
          ????}
          ????$inp.addEventListener('keyup',?$inp.handle)
          ??},
          ??unbind:?function?(el)?{
          ????el.$inp.removeEventListener('keyup',?el.$inp.handle)
          ??},
          }

          export?default?emoji

          使用:將需要校驗的輸入框加上?v-emoji?即可

          <template>
          ??<input?type="text"?v-model="note"?v-emoji?/>
          template>

          v-LazyLoad

          背景:在類電商類項目,往往存在大量的圖片,如 banner 廣告圖,菜單導(dǎo)航圖,美團(tuán)等商家列表頭圖等。圖片眾多以及圖片體積過大往往會影響頁面加載速度,造成不良的用戶體驗,所以進(jìn)行圖片懶加載優(yōu)化勢在必行。

          需求:實現(xiàn)一個圖片懶加載指令,只加載瀏覽器可見區(qū)域的圖片。

          思路:

          1. 圖片懶加載的原理主要是判斷當(dāng)前圖片是否到了可視區(qū)域這一核心邏輯實現(xiàn)的
          2. 拿到所有的圖片 Dom ,遍歷每個圖片判斷當(dāng)前圖片是否到了可視區(qū)范圍內(nèi)
          3. 如果到了就設(shè)置圖片的?src?屬性,否則顯示默認(rèn)圖片

          圖片懶加載有兩種方式可以實現(xiàn),一是綁定?srcoll?事件進(jìn)行監(jiān)聽,二是使用?IntersectionObserver?判斷圖片是否到了可視區(qū)域,但是有瀏覽器兼容性問題。

          下面封裝一個懶加載指令兼容兩種方法,判斷瀏覽器是否支持?IntersectionObserverAPI,如果支持就使用?IntersectionObserver?實現(xiàn)懶加載,否則則使用?srcoll?事件監(jiān)聽 + 節(jié)流的方法實現(xiàn)。

          const?LazyLoad?=?{
          ??//?install方法
          ??install(Vue,?options)?{
          ????const?defaultSrc?=?options.default
          ????Vue.directive('lazy',?{
          ??????bind(el,?binding)?{
          ????????LazyLoad.init(el,?binding.value,?defaultSrc)
          ??????},
          ??????inserted(el)?{
          ????????if?(IntersectionObserver)?{
          ??????????LazyLoad.observe(el)
          ????????}?else?{
          ??????????LazyLoad.listenerScroll(el)
          ????????}
          ??????},
          ????})
          ??},
          ??//?初始化
          ??init(el,?val,?def)?{
          ????el.setAttribute('src',?val)
          ????el.setAttribute('src',?def)
          ??},
          ??//?利用IntersectionObserver監(jiān)聽el
          ??observe(el)?{
          ????var?io?=?new?IntersectionObserver((entries)?=>?{
          ??????const?realSrc?=?el.dataset.src
          ??????if?(entries[0].isIntersecting)?{
          ????????if?(realSrc)?{
          ??????????el.src?=?realSrc
          ??????????el.removeAttribute('src')
          ????????}
          ??????}
          ????})
          ????io.observe(el)
          ??},
          ??//?監(jiān)聽scroll事件
          ??listenerScroll(el)?{
          ????const?handler?=?LazyLoad.throttle(LazyLoad.load,?300)
          ????LazyLoad.load(el)
          ????window.addEventListener('scroll',?()?=>?{
          ??????handler(el)
          ????})
          ??},
          ??//?加載真實圖片
          ??load(el)?{
          ????const?windowHeight?=?document.documentElement.clientHeight
          ????const?elTop?=?el.getBoundingClientRect().top
          ????const?elBtm?=?el.getBoundingClientRect().bottom
          ????const?realSrc?=?el.dataset.src
          ????if?(elTop?-?windowHeight?0?&&?elBtm?>?0)?{
          ??????if?(realSrc)?{
          ????????el.src?=?realSrc
          ????????el.removeAttribute('src')
          ??????}
          ????}
          ??},
          ??//?節(jié)流
          ??throttle(fn,?delay)?{
          ????let?timer
          ????let?prevTime
          ????return?function?(...args)?{
          ??????const?currTime?=?Date.now()
          ??????const?context?=?this
          ??????if?(!prevTime)?prevTime?=?currTime
          ??????clearTimeout(timer)

          ??????if?(currTime?-?prevTime?>?delay)?{
          ????????prevTime?=?currTime
          ????????fn.apply(context,?args)
          ????????clearTimeout(timer)
          ????????return
          ??????}

          ??????timer?=?setTimeout(function?()?{
          ????????prevTime?=?Date.now()
          ????????timer?=?null
          ????????fn.apply(context,?args)
          ??????},?delay)
          ????}
          ??},
          }

          export?default?LazyLoad

          使用,將組件內(nèi) ?標(biāo)簽的?src?換成?v-LazyLoad

          <img?v-LazyLoad="xxx.jpg"?/>

          v-permission

          背景:在一些后臺管理系統(tǒng),我們可能需要根據(jù)用戶角色進(jìn)行一些操作權(quán)限的判斷,很多時候我們都是粗暴地給一個元素添加?v-if / v-show?來進(jìn)行顯示隱藏,但如果判斷條件繁瑣且多個地方需要判斷,這種方式的代碼不僅不優(yōu)雅而且冗余。針對這種情況,我們可以通過全局自定義指令來處理。

          需求:自定義一個權(quán)限指令,對需要權(quán)限判斷的 Dom 進(jìn)行顯示隱藏。

          思路:

          1. 自定義一個權(quán)限數(shù)組
          2. 判斷用戶的權(quán)限是否在這個數(shù)組內(nèi),如果是則顯示,否則則移除 Dom
          function?checkArray(key)?{
          ??let?arr?=?['1',?'2',?'3',?'4']
          ??let?index?=?arr.indexOf(key)
          ??if?(index?>?-1)?{
          ????return?true?//?有權(quán)限
          ??}?else?{
          ????return?false?//?無權(quán)限
          ??}
          }

          const?permission?=?{
          ??inserted:?function?(el,?binding)?{
          ????let?permission?=?binding.value?//?獲取到?v-permission的值
          ????if?(permission)?{
          ??????let?hasPermission?=?checkArray(permission)
          ??????if?(!hasPermission)?{
          ????????//?沒有權(quán)限?移除Dom元素
          ????????el.parentNode?&&?el.parentNode.removeChild(el)
          ??????}
          ????}
          ??},
          }

          export?default?permission

          使用:給?v-permission?賦值判斷即可

          <div?class="btns">
          ??
          ??<button?v-permission="'1'">權(quán)限按鈕1button>
          ??
          ??<button?v-permission="'10'">權(quán)限按鈕2button>
          div>

          vue-waterMarker

          需求:給整個頁面添加背景水印

          思路:

          1. 使用?canvas?特性生成?base64?格式的圖片文件,設(shè)置其字體大小,顏色等。
          2. 將其設(shè)置為背景圖片,從而實現(xiàn)頁面或組件水印效果
          function?addWaterMarker(str,?parentNode,?font,?textColor)?{
          ??//?水印文字,父元素,字體,文字顏色
          ??var?can?=?document.createElement('canvas')
          ??parentNode.appendChild(can)
          ??can.width?=?200
          ??can.height?=?150
          ??can.style.display?=?'none'
          ??var?cans?=?can.getContext('2d')
          ??cans.rotate((-20?*?Math.PI)?/?180)
          ??cans.font?=?font?||?'16px?Microsoft?JhengHei'
          ??cans.fillStyle?=?textColor?||?'rgba(180,?180,?180,?0.3)'
          ??cans.textAlign?=?'left'
          ??cans.textBaseline?=?'Middle'
          ??cans.fillText(str,?can.width?/?10,?can.height?/?2)
          ??parentNode.style.backgroundImage?=?'url('?+?can.toDataURL('image/png')?+?')'
          }

          const?waterMarker?=?{
          ??bind:?function?(el,?binding)?{
          ????addWaterMarker(binding.value.text,?el,?binding.value.font,?binding.value.textColor)
          ??},
          }

          export?default?waterMarker

          使用,設(shè)置水印文案,顏色,字體大小即可

          <template>
          ??<div?v-waterMarker="{text:'lzg版權(quán)所有',textColor:'rgba(180,?180,?180,?0.4)'}">div>
          template>

          v-draggable

          需求:實現(xiàn)一個拖拽指令,可在頁面可視區(qū)域任意拖拽元素。

          思路:

          1. 設(shè)置需要拖拽的元素為相對定位,其父元素為絕對定位。
          2. 鼠標(biāo)按下(onmousedown)時記錄目標(biāo)元素當(dāng)前的?left?和?top?值。
          3. 鼠標(biāo)移動(onmousemove)時計算每次移動的橫向距離和縱向距離的變化值,并改變元素的?left?和?top?值
          4. 鼠標(biāo)松開(onmouseup)時完成一次拖拽
          const?draggable?=?{
          ??inserted:?function?(el)?{
          ????el.style.cursor?=?'move'
          ????el.onmousedown?=?function?(e)?{
          ??????let?disx?=?e.pageX?-?el.offsetLeft
          ??????let?disy?=?e.pageY?-?el.offsetTop
          ??????document.onmousemove?=?function?(e)?{
          ????????let?x?=?e.pageX?-?disx
          ????????let?y?=?e.pageY?-?disy
          ????????let?maxX?=?document.body.clientWidth?-?parseInt(window.getComputedStyle(el).width)
          ????????let?maxY?=?document.body.clientHeight?-?parseInt(window.getComputedStyle(el).height)
          ????????if?(x?0)?{
          ??????????x?=?0
          ????????}?else?if?(x?>?maxX)?{
          ??????????x?=?maxX
          ????????}

          ????????if?(y?0)?{
          ??????????y?=?0
          ????????}?else?if?(y?>?maxY)?{
          ??????????y?=?maxY
          ????????}

          ????????el.style.left?=?x?+?'px'
          ????????el.style.top?=?y?+?'px'
          ??????}
          ??????document.onmouseup?=?function?()?{
          ????????document.onmousemove?=?document.onmouseup?=?null
          ??????}
          ????}
          ??},
          }
          export?default?draggable

          使用: 在 Dom 上加上 v-draggable 即可

          <template>
          ??<div?class="el-dialog"?v-draggable>div>
          template>

          關(guān)注數(shù):10億+?文章數(shù):10億+
          粉絲量:10億+?點擊量:10億+

          ?


          懸賞博主專區(qū)請掃描這里


          喜愛數(shù):?1億+?發(fā)帖數(shù):?1億+
          回帖數(shù):?1億+?結(jié)貼率:?99.9%


          —————END—————



          喜歡本文的朋友,歡迎關(guān)注公眾號?程序員哆啦A夢,收看更多精彩內(nèi)容

          點個[在看],是對小達(dá)最大的支持!


          如果覺得這篇文章還不錯,來個【分享、點贊、在看】三連吧,讓更多的人也看到~

          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91人妻网站 | www亚洲色 | 欧美成人精品一区二区三区在线观看 | 国产91美女被操网站 | 成人免费视频 国产免费麻豆。 |