需要先学习一下vue的自定义指令
Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
举个聚焦输入框的例子
当页面加载时,该元素将获得焦点。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能:
// 注册一个全局自定义指令`v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
如果想注册局部指令,组件中也接受一个 directives 的选项:
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
钩子函数
- bind: 只调用一次,指令第一次绑定到元素时调用,可以定义一个在绑定时执行一次的初始化动作。
- inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
- update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值。
- componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
- unbind: 只调用一次, 指令与元素解绑时调用。
钩子函数参数
-
el:指令所绑定的元素,可以用来直接操作 DOM。 -
binding:一个对象,包含以下 property:name:指令名value:指令的绑定值,例如:v-my-directive="1 + 1"中,绑定值为2。oldValue:指令绑定的前一个值,仅在update和componentUpdated钩子中可用。无论值是否改变都可用。expression:字符串形式的指令表达式。例如v-my-directive="1 + 1"中,表达式为"1 + 1"。arg:传给指令的参数,可选。例如v-my-directive:foo中,参数为"foo"。modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar中,修饰符对象为{ foo: true, bar: true }。
-
vnode:Vue 编译生成的新虚拟节点。 -
oldVnode:上一个虚拟节点,仅在update和componentUpdated钩子中可用。
除了
el之外,其它参数都应该是只读的,切勿进行修改
实现几个实用的 Vue 自定义指令
- 输入框防抖指令
v-debounce - 权限校验指令
v-premission - 拖拽指令
v-draggable
v-debounce
背景:在开发中,有些提交保存按钮有时候会在短时间内被点击多次,这样就会多次重复请求后端接口,造成数据的混乱,比如新增表单的提交按钮,多次点击就会新增多条重复的数据。
需求:防止按钮在短时间内被多次点击,使用防抖函数限制规定时间内只能点击一次。
思路:
- 定义一个延迟执行的方法,如果在延迟时间内再调用该方法,则重新计算执行时间。
- 将时间绑定在 click 方法上。
//第一步 模块化 创建一个debounce.js文件
export default {
bind(el,bindings) {
let timer
el.addEventListener('click', () => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
bindings.value()
}, 1000)
})
}
}
//在使用的地方引入 app.vue
<el-button v-has=" 'creat' " v-debounce="click">编辑</el-button>
<script>
import debounce from "@/directives/debounce";
export default{
directives:{
debounce: debounce
},
methods:{
click() {
console.log('focus实现了一次')
}
}
}
</script>
v-permission
背景:在一些后台管理系统,我们可能需要根据用户角色进行一些操作权限的判断,很多时候我们都是粗暴地给一个元素添加 v-if / v-show 来进行显示隐藏,但如果判断条件繁琐且多个地方需要判断,这种方式的代码不仅不优雅而且冗余。针对这种情况,我们可以通过全局自定义指令来处理。
需求:自定义一个权限指令,对需要权限判断的 Dom 进行显示隐藏。
思路:
- 自定义一个权限数组
- 判断用户的权限是否在这个数组内,如果是则显示,否则则移除 Dom
//第一步 模块化 创建一个has.js文件
export default {
bind(el,bindings){
const permission = localStorage.getItem('permissions')
const needPermission = bindings.value
const bool = permission.includes(needPermission)
if(!bool){
// el.style.display ='none' 这个只是隐藏
//渲染是异步的
setTimeout(()=>{
el.parentNode.removeChild(el)
},0)
}
}
}
//在使用的地方引入 app.vue
<div class="parent" style="background: pink ; height: 50px ; padding: 15px;display: flex">
<el-button v-has=" 'creat' " v-debounce="click">编辑</el-button>
<el-button v-has="'edit'">添加</el-button>
<el-button v-has="'delete'">删除</el-button>
</div>
<script>
import hasDirective from '@/directives/has'
export default{
directives:{
has: hasDirective,
}
</script>
v-draggable
需求:实现一个拖拽指令,可在页面可视区域任意拖拽元素。
思路:
- 设置需要拖拽的元素为相对定位,其父元素为绝对定位。
- 鼠标按下
(onmousedown)时记录目标元素当前的left和top值。 - 鼠标移动
(onmousemove)时计算每次移动的横向距离和纵向距离的变化值,并改变元素的left和top值 - 鼠标松开
(onmouseup)时完成一次拖拽
const draggable = {
inserted: function (el) {
el.style.cursor = 'move'
el.onmousedown = 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
}
}
},
}
export default draggable
<template>
<div class="el-dialog" v-draggable></div>
</template>