一、自定义指令基础
1、vue原生指令
- 样式示例:
v-name(指令名称):arg(指令传递参数).stop(指令修饰符)=”value(指令值)“ - vue指令常见修饰符:
- .stop - 调用 event.stopPropagation()。阻止事件冒泡
- .prevent - 调用 event.preventDefault()。阻止事件默认行为
- .native - 监听组件根元素的原生事件。
2、自定义指令包含多个钩子函数:
- bind:只调用一次,指令第一次绑定到元素时调用。进行一次性的初始化设置
- inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
- update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
- componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
- unbind:只调用一次,指令与元素解绑时调用。 最经常用到的钩子函数:
vue.directive('自定义指令', {
bind: function (el, binding, vnode) {
}
unbind: function (el, binding, vnode) {
}
})
3、钩子函数参数
- el:代表着指令绑定的元素,直接操作DOM;
- binding:存在一堆的property
- name:指令名称;
- value:指令值,可以进行表达式计算;
- expression:指令表达式。例如:v-my-directive="1 + 1",字符串形式;个人理解表达式也可以作为指令值
- arg:指令参数;
data () {
return {
arg: {
name: 'love_myself',
age: 22
}
}}
- modifiers:指令修饰符。例如:v-my-directive.foo,修饰符对象为 { foo: true };
- vnode:Vue编译生成的虚拟节点;
- oldValue:指令绑定的前一个值:适用于update和componentUpdated钩子函数中;
- oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
二、封装常见的自定义指令
1、长按指令:[v-longtouch]
使用场景:长按图片、或长按二维码
import Vue from 'vue'
Vue.directive('longtouch', {
bind: function (el, binding, vnode) {
if (typeof binding.value !== function) {
const componentName = vnode.context.name
let warn = `[longtouch:] provided expression '${binding.expression}' is not a function, but has to be`
if (componentName) { warn += `found in ${componentName}`}
console.warn(warn)
}
let longtouchTimer = null
const start = (e) => {
// e.button === 0:左键
if (e.type === 'click' && e.button !== 0) return
longtouchTimer = setTimeout(() => {
binding.value()
}, 600)
}
const cancel = () => {
if (longtouchTimer !== null) {
clearTimeout(longtouchTimer)
longtouchTimer = null
}
}
el.addEventListener('touchstart', start)
el.addEventListener('mousedown', start)
el.addEventListener('click', cancel)
el.addEventListener('touchend', cancel)
el.addEventListener('touchcancel', cancel)
el.addEventListener('mouseout', cancel)
}
})
// 使用步骤与正常的v-指令用法是一样的
2、滑动指令:[v-scroll]
使用场景:下拉滑动页面的时候
import Vue from 'vue'
Vue.directive('scroll', {
inserted: function (el, binding) {
const f = function (evt) {
if (binding.value(evt, el)) {
window.removeEventListener('scroll', f)
}
}
window.addEventListener('scroll', f)
}
})
// 使用步骤与正常的v-指令用法是一样的
3、滑动固定指令[v-scrollFixed]
使用场景:滑动页面,使得某内容固定在合适位置
- 模式一:页面中自定义指令,直接使用
// 自定义指令
Vue.directive('scrollFixed', {
bind: function (el, binding) {
if (binding.modifiers.prevent) {
el.addEventListener('touchmove', eventPreventDefault)
}
if (binding.modifiers.stop) {
el.addEventListener('touchmove', eventStopPropagation)
}
fixedBody()
},
unbind: function (el, binding) {
if (binding.modifiers.prevent) {
el.removeEventListener('touchmove', eventPreventDefault)
}
if (binding.modifiers.stop) {
el.removeEventListener('touchmove', eventStopPropagation)
}
looseBody()
}
})
// 抽离方法
function eventPreventDefault (e) {
e.preventDefault()
}
function eventStopPropagation (e) {
e.stopPropagation()
}
function fixedBody () {
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop
document.body.style.cssText += 'position:fixed;width:100%;top:-' + scrollTop + 'px;'
}
function looseBody () {
var body = document.body
var top = body.style.top
body.style.position = ''
body.style.width = ''
// eslint-disable-next-line no-multi-assign
document.body.scrollTop = document.documentElement.scrollTop = -parseInt(top)
body.style.top = ''
}
使用步骤与正常的v-指令用法是一样的
- 模式二:以插件形式封装OK,注入到vue实例上去,在自己组件中使用
// 1. 处理插件OK,导出去
const scrollFixedPlugin = {
install (Vue) {
Vue.directive('scrollFixed', {
bind: function (el, binding) {
if (binding.modifiers.prevent) {
el.addEventListener('touchmove', eventPreventDefault)
}
if (binding.modifiers.stop) {
el.addEventListener('touchmove', eventStopPropagation)
}
fixedBody()
},
unbind: function (el, binding) {
if (binding.modifiers.prevent) {
el.removeEventListener('touchmove', eventPreventDefault)
}
if (binding.modifiers.stop) {
el.removeEventListener('touchmove', eventStopPropagation)
}
looseBody()
}
})
}
}
function eventPreventDefault (e) {
e.preventDefault()
}
function eventStopPropagation (e) {
e.stopPropagation()
}
function fixedBody () {
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop
document.body.style.cssText += 'position:fixed;width:100%;top:-' + scrollTop + 'px;'
}
function looseBody () {
var body = document.body
var top = body.style.top
body.style.position = ''
body.style.width = ''
// eslint-disable-next-line no-multi-assign
document.body.scrollTop = document.documentElement.scrollTop = -parseInt(top)
body.style.top = ''
}
export default scrollFixedPlugin
2. 注入vue实例,use实例
import Vue from 'vue'
import scrollFixedPlugin from '@/utils/scrollFixed.js'
Vue.use(scrollFixedPlugin)
3. 使用方式
.model(v-scrollFixed v-if="flag")