日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。