當(dāng)前位置:
首頁 >
Vue 自定义的几个指令
發(fā)布時間:2023/12/13
30
生活家
生活随笔
收集整理的這篇文章主要介紹了
Vue 自定义的几个指令
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
Vue 自定義指令: Vue.directive(id, [definition])
參數(shù):
{string}: id
{Function | Object} [definition]
用法:
// 注冊
Vue.directive('my-directive', {
bind: function () {},
inserted: function () {},
update: function () {},
componentUpdated: function () {},
unbind: function () {}
})
// 注冊 (指令函數(shù))
Vue.directive('my-directive', function () {
// 這里將會被 `bind` 和 `update` 調(diào)用
})
// getter,返回已注冊的指令
var myDirective = Vue.directive('my-directive')
自定義指令有五個鉤子函數(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)用。
結(jié)合工作中的一些使用場景,我們可以自定義一些常用的指令。
directives.js:
// v-copy
const copy = {
bind (el, { value }) {
el.$value = value
el.handler = () => {
if (!el.$value) {
// 值為空時,提示 可根據(jù)UI 另設(shè)計
console.log('無復(fù)制內(nèi)容')
return
}
// 動態(tài)創(chuàng)建textarea
const textarea = document.createElement('textarea')
// 將該textarea 設(shè)為 readonly 防止 iOS 下自動喚起鍵盤, 同時將 textarea 移除可視區(qū)域
textarea.readonly = 'readonly'
textarea.style.display = 'none'
textarea.style.left = '-9999px'
textarea.value = el.$value
document.body.appendChild(textarea)
textarea.select()
const result = document.execCommand('Copy')
if (result) {
// 可根據(jù)UI 另設(shè)計
console.log('復(fù)制成功')
}
document.body.removeChild(textarea)
}
// 綁定點擊事件 一鍵 copy
el.addEventListener('click', el.handler)
},
componentUpdated(el, { value }) {
el.$value = value
},
// 指令與元素解綁時 移除綁定事件
unbind (el) {
el.removeEventListener('click', el.handler)
}
}
/**
*
* <template>
<button v-copy="copyText">復(fù)制</button>
</template>
<script>
export default {
data() {
return {
copyText: 'a copy directives',
}
},
}
</script>
*/
// v-longpress
const longpress = {
bind (el, binding, vNode) {
if (typeof binding.value !== 'function') {
throw 'callback must be a function'
}
let pressTimer = null
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
}
}
const handler = (e) => {
binding.value(e)
}
// 監(jiān)聽事件
// 添加事件監(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)
},
componentUpdated(el, { value }) {
el.$value = value
},
unbind (el) {
el.removeEventListener('click', el.handler)
}
}
/**
* <template>
<button v-longpress="longpress">長按</button>
</template>
<script>
export default {
methods: {
longpress () {
alert('長按指令生效')
}
}
}
</script>
*
*/
// 防抖函數(shù)限制規(guī)定 規(guī)定時間內(nèi)之只能點擊一次
const debounce = {
inserted (el, binding) {
let timer
el.addEventListener('click', () => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
binding.value()
}, 1000)
})
}
}
/**
* <template>
<button v-debounce="debounceClick">防抖</button>
</template>
<script>
export default {
methods: {
debounceClick () {
console.log('只觸發(fā)一次')
}
}
}
</script>
*/
// 限制輸入內(nèi)容 不能輸入表情和特殊字符,只能時數(shù)字或字母等 常規(guī)方法就是在 change 中做正則校驗
/**
* <template>
<input type="text" v-model="note" @change="vaidateEmoji" />
</template>
<script>
export default {
methods: {
vaidateEmoji() {
var reg = /[^u4E00-u9FA5|d|a-zA-Z|
s,.?!,。?!…—&$=()-+/*{}[]]|s/g
this.note = this.note.replace(reg, '')
},
},
}
</script>
*/
// 按照正則校驗的規(guī)則來設(shè)計一個指令
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 (el, binding, vNode) {
// 正則規(guī)則可根據(jù)需求自定義
var regRule = /[^u4E00-u9FA5|d|a-zA-Z|
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 (el) {
el.$inp.removeEventListener('keyup', el.$inp.handle)
}
}
/**
* <template>
<input type="text" v-model="note" v-emoji />
</template>
*/
// 實現(xiàn)一個圖片懶加載指令,只加載瀏覽器可見區(qū)域的圖片
/**
* 圖片懶加載的原理主要是判斷當(dāng)前圖片是否到了可視區(qū)域這一核心邏輯實現(xiàn)的
拿到所有的圖片 Dom ,遍歷每個圖片判斷當(dāng)前圖片是否到了可視區(qū)范圍內(nèi)
如果到了就設(shè)置圖片的 src 屬性,否則顯示默認(rèn)圖片
圖片懶加載有兩種方式可以實現(xiàn),一是綁定 srcoll 事件進(jìn)行監(jiān)聽,
二是使用 IntersectionObserver 判斷圖片是否到了可視區(qū)域,但是有瀏覽器兼容性問題。
下面封裝一個懶加載指令兼容兩種方法,判斷瀏覽器是否支持 IntersectionObserver API,
如果支持就使用 IntersectionObserver 實現(xiàn)懶加載,否則則使用 srcoll 事件監(jiān)聽 + 節(jié)流的方法實現(xiàn)。
*/
const Lazyload = {
install (Vue, options) {
const defalutSrc = options.default
Vue.directive('lazy', {
bind(el, binding) {
Lazyload.init(el, binding.value, defalutSrc)
},
inserted (el) {
if (IntersectionObserver) {
Lazyload.observe(el)
} else {
Lazyload.listenerScroll(el)
}
}
})
},
// 初始化
init (el, val, def) {
el.setAttribute('data-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('data-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('data-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(() => {
prevTime = Date.now()
timer = null
fn.apply(context, args)
}, delay)
}
}
}
/**
* <img v-LazyLoad="xxx.jpg" />
*/
/**
* 需求:自定義一個權(quán)限指令,對需要權(quán)限判斷的 Dom 進(jìn)行顯示隱藏。
自定義一個權(quán)限數(shù)組
判斷用戶的權(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
} else {
return false
}
}
const permission = {
inserted (el, binding) {
let permission = binding.value
if (permission) {
let hasPermission = checkArray(permission)
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
}
}
}
/**
* <div class="btns">
<!-- 顯示 -->
<button v-permission="'1'">權(quán)限按鈕1</button>
<!-- 不顯示 -->
<button v-permission="'10'">權(quán)限按鈕2</button>
</div>
*/
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 (el, binding) {
addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor)
}
}
/**
* <template>
<div v-waterMarker="{text:'lzg版權(quán)所有',textColor:'rgba(180, 180, 180, 0.4)'}"></div>
</template>
*/
/**
* 實現(xiàn)一個拖拽指令,可以在頁面可視區(qū)域任意拖拽元素
*
* 設(shè)置需要拖拽的元素為相對定位,其父元素為絕對定位。
鼠標(biāo)按下(onmousedown)時記錄目標(biāo)元素當(dāng)前的 left 和 top 值。
鼠標(biāo)移動(onmousemove)時計算每次移動的橫向距離和縱向距離的變化值,并改變元素的 left 和 top 值
鼠標(biāo)松開(onmouseup)時完成一次拖拽
*/
const draggable = {
inserted (el) {
el.style.cursor = 'move'
el.onmosedown = 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
}
}
}
}
/**
* <template>
<div class="el-dialog" v-draggable></div>
</template>
*/
export default {
copy,
longpress,
debounce,
emoji,
Lazyload,
permission,
waterMarker,
draggable
}
引入方法:
新建 directives/index.js 文件:
import {
copy,
longpress,
debounce,
emoji,
Lazyload,
permission,
waterMarker,
draggable
} from './directives'
const directives = {
copy,
longpress,
debounce,
emoji,
Lazyload,
permission,
waterMarker,
draggable
}
export default {
install(Vue) {
Object.keys(directives).forEach((key) => {
Vue.directive(key, directives[key])
})
}
}
在 main.js 中注冊 Directives 插件:
import Vue from 'vue' import Directives from './directives' Vue.use(Directives)
注:內(nèi)容來自于網(wǎng)絡(luò),侵刪。
總結(jié)
以上是生活随笔為你收集整理的Vue 自定义的几个指令的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++学习 优雅的实现对象到文件的序列化
- 下一篇: oracle根据身份证号码 计算年龄、性