vue3 自定义指令[directives]
- 自定义指令主要是为了重用涉及普通元素的底层DOM访问的逻辑。简单来说就是对复杂的dom访问逻辑的封装,更偏向js的逻辑复用,这与组件的概念是不一样的
指令基本语法
指令钩子
const myDirective = {
created(el, binding, vnode, prevVnode) {
},
beforeMount(el, binding, vnode, prevVnode) {},
mounted(el, binding, vnode, prevVnode) {},
beforeUpdate(el, binding, vnode, prevVnode) {},
updated(el, binding, vnode, prevVnode) {},
beforeUnmount(el, binding, vnode, prevVnode) {},
unmounted(el, binding, vnode, prevVnode) {}
}
指令钩子参数
- el: 指令绑定到的元素。这可以用于直接操作DOM。
- binding: 一个对象,包含以下属性
- value: 传递给指令的值。例如:
v-my-directive='999'中,value===999
- arg: 传递给指令的参数 (如果有的话)。例如在 v-my-directive:loading 中,参数是 "loading"
- instance: 使用该指令的组件实例。可以用来访问组件的变量和方法
- oldValue: 之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用
- modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。
- vnode:代表绑定元素的底层 VNode。
- prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。
指令可以做哪些事情
- 操作渲染dom,比如vFocus、vMyHtml、数据格式转换
- 图片懒加载
- 上拉加载更多
- 下拉刷新
- ......
Directives实战
v-focus: 组件内
<input v-focus>
export default {
directives: {
focus: {
mounted(el) {
el.focus()
}
}
}
}
<script setup>
const vFocus = {
mounted: el => el.focus()
}
</script>
v-my-html
const app = createApp({})
app.directives('myHtml',(el, binding) => {
el.innerHTML = binding.value.split(/\n|\r|\r\n/).join('<br/>')
})
v-loadmore
let loading = null
let callback = null
let myEl = null
let instance = null
let threshold = 50
let monitorEl = 'body'
let nowTime = 0
function getScrollTop () {
if (monitorEl === 'nowTag') {
return myEl.scrollTop
} else {
return document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
}
}
function getScrollHeight () {
if (monitorEl === 'nowTag') {
return myEl.scrollHeight
} else {
return document.body.clientHeight || document.documentElement.clientHeight
}
}
function getOffsetTop () {
if (monitorEl === 'nowTag') {
return myEl.offsetTop
} else {
return 0
}
}
function getHeight () {
if (monitorEl === 'nowTag') {
return myEl.clientHeight
} else {
return window.innerHeight
}
}
function myScroll () {
if (instance[loading] > 0) {
return
} else if (nowTime + 16 > + new Date()) {
return
} else {
nowTime = + new Date()
}
let height = 0
let offsetTop = 0
let scrollHeight = 0
let scroll = 0
scroll = getScrollTop()
scrollHeight = getScrollHeight()
height = getHeight()
offsetTop = getOffsetTop()
let h = scrollHeight + offsetTop - height - scroll
if (h < threshold && h >= 0) {
callback()
}
}
const app = createApp({})
app.directive('loadmore', {
mounted (el, binding) {
loading = binding.arg
callback = binding.value
myEl = el
instance = binding.instance
threshold = el.getAttribute('threshold') || 200
monitorEl = el.getAttribute('monitorEl') || 'body'
if (monitorEl === 'nowTag') {
myEl.onscroll = myScroll
} else {
window.addEventListener('scroll', myScroll, false)
}
},
unmounted () {
if (monitorEl === 'nowTag') {
window.removeEventListener('scroll', myScroll, false)
}
}
})
玫瑰花宝典