分享项目中用到的 vue 自定义指令
- 表单防止重复提交
- 图片懒加载
- 一键 copy 的功能
自定义指令-点击事件节流
Vue.directive("throttle", {
bind: (el, binding) => {
let throttleTime = binding.value
!throttleTime && (throttleTime = 2000)
let id
el.addEventListener("click", (evt) => {
if (!id) {
id = setTimeout(() => {
id = null
}, throttleTime)
} else {
evt.stopImmediatePropagation()
}
// 注意这里要用捕获模式
}, true)
}
})
用法:
<button v-throttle="2000" @click="onClick">submit</button>
通过在浏览器控制台getEventListeners查看按钮的事件回调列表,发现在bind里面绑定的回调,顺序在@click绑定的回调之后,可以推断得出,@click的回调会先于bind的回调绑定到节点。无论钩子是bind或者inserted都一样。
当捕获模式时,节点的回调列表是从后向前,倒序执行的,所以bind里绑定的回调会先执行,stopImmediatePropagation可以阻断@click的回调执行。
反之,当冒泡模式时,回调列表是从前往后执行的,所以会先执行@click的回调,再到bind的回调。
自定义指令-图片懒加载
通过IntersectionObserver、或者scroll事件来判断图片是否需要加载。
const LazyLoad = {
install(Vue, options) {
const defaultSrc = options.defaultSrc
Vue.directive("lazyload", {
bind(el, binding) {
LazyLoad.init(el, binding.value, defaultSrc)
},
inserted(el) {
if ('IntersectionObserver' in window) {
LazyLoad.observe(el)
} else {
LazyLoad.listenScroll(el)
}
}
})
},
init(el, src, defaultSrc) {
el.setAttribute("data-src", src)
el.setAttribute('src', defaultSrc)
},
observe(el) {
const observe = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
const src = el.dataset.src
src && el.setAttribute("src", src) && el.removeAttribute('data-src')
}
})
observe.observe(el)
},
load(el) {
const height = document.documentElement.clientHeight
const {
top, bottom
} = el.getBoundingClientRect()
if (top < height && bottom > 0) {
const src = el.dataset.src
src && el.setAttribute("src", src) && el.removeAttribute("data-src")
}
},
listenScroll(el) {
LazyLoad.load(el)
const scrollHandler = LazyLoad.throttle(LazyLoad.load)
document.addEventListener('scroll', () => scrollHandler(el))
},
throttle(fn, delay = 300) {
let id
return function (...args) {
if (!id) {
id = setTimeout(() => {
id = null
fn.apply(this, args)
}, delay)
}
}
}
}
export default LazyLoad
用法
import Vue from 'vue'
import Lazyload from './Lazyload.js'
Vue.use(Lazyload, { defaultSrc: 'loadingimgurl' }),
<img v-lazyload="imgurl" />
自定义指令-点击复制
const Copy = {
bind(el, { value }) {
el.$value = value
el.handler = () => {
if (!el.$value) return
const textarea = document.createElement("input")
textarea.setAttribute("readOnly", 'readOnly')
textarea.style.position = 'absolute'
textarea.style.left = '-9999px'
textarea.value = el.$value
document.documentElement.appendChild(textarea)
textarea.select()
const result = document.execCommand("Copy")
if (result) {
// 复制成功
}
document.documentElement.removeChild(textarea)
}
el.addEventListener("click", el.handler)
},
componentUpdated(el, { value }) {
el.$value = value
}
}
export default Copy
用法
import Vue from 'vue'
import Copy from './copy.js'
Vue.directive('copy', Copy)
<button v-copy="msg">copy</button>