directive指令是什么
除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令。
然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
directive指令有什么用
实际应用-- 可以通过指令知道什么时候dom创建完成,从而进行依赖dom的库的初始化工作
directive指令怎么用
举个改变背景色的例子,如下:
<!--自定义指令 v-hello-->
<div v-hello="whichColor">123</div><br><br>
data() {
return {
whichColor: 'yellow',
}
},
directives: {
//指令的生命周期函数
hello: {
// 指令的定义
inserted(el, binding, VNode) {
el.style.background=binding.value
},
},
}
运行结果:
- el : 指令所绑定的元素,可以用来直接操作DOM
- binding: 一个对象,包含指令的很多信息
- vnode: 虚拟节点
详细介绍
自定义指令的生命周期(钩子函数)
| 钩子函数 | Vue 3 | 说明 |
|---|---|---|
| - | created 新增! | 在元素的 attribute 或事件监听器被应用之前调用。 |
| bind | beforeMount | 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。 |
| inserted | mounted | 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 |
| - | beforeUpdate 新增! | 在元素本身被更新之前调用,与组件的生命周期钩子十分相似。 |
| update | 移除!该钩子与 updated 有太多相似之处,因此它是多余的。请改用 updated。 | 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。 |
| componentUpdated | updated | 指令所在组件的 VNode 及其子 VNode 全部更新后调用。 |
| - | beforeUnmount 新增! | 与组件的生命周期钩子类似,它将在元素被卸载之前调用。 |
| unbind | unmounted | 只调用一次,指令与元素解绑时调用。 |
Vue 3 最终的API如下:
const MyDirective = {
created(el, binding, vnode, prevVnode) {}, // 新增
beforeMount() {},
mounted() {},
beforeUpdate() {}, // 新增
updated() {},
beforeUnmount() {}, // 新增
unmounted() {}
}
函数的参数
指令钩子函数会被传入以下参数:
-
el:指令所绑定的元素,可以用来直接操作 DOM。 -
binding:一个对象,包含以下 property:name:指令名,不包括v-前缀。value:指令的绑定值,例如:v-my-directive="1 + 1"中,绑定值为2。oldValue:指令绑定的前一个值,仅在update和componentUpdated钩子中可用。无论值是否改变都可用。expression:字符串形式的指令表达式。。arg:传给指令的参数,可选。modifiers:一个包含修饰符的对象。
-
vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。 -
oldVnode:上一个虚拟节点,仅在update和componentUpdated钩子中可用。
为了说明以上参数实现的demo:
<h2>v-my-local</h2>
<p v-my-local:argFoo.modifiersTest="{color:colorLocal,fontSize:fontSizeLocal}">我是v-my-local指令</p>
data() {
return {
colorLocal:'red',
fontSizeLocal:'12',
}
},
directives: {
myLocal:{
bind(el,binding,vnode) {
console.log('myLocal bind')
console.log(el,binding,vnode)
el.style.color=binding.value.color
el.style.fontSize=binding.value.fontSize+'px'
console.log('------------------------------')
},
update(el, binding, vnode) {
console.log('myLocal update')
console.log(el,binding,vnode)
el.style.color=binding.value.color
el.style.fontSize=binding.value.fontSize+'px'
console.log('------------------------------')
}
}
}
colorLocal 修改成 blue,运行结果如下:
使用场景
自动获取焦点
公司项目中,有自动获取焦点的需求。
虽然iview等组件库也提供了,自动获取焦点属性autofocus,但是使用是还是有问题。
如下:
<template>
<div class="hello">
<h2>autofocus</h2>
autofocus: <Input v-model="value" autofocus placeholder="Enter something..." />
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data() {
return {
value: '',
}
},
created() {
window.d = this
},
}
</script>
切换页面路由时,autofocus不起作用,刷新页面autofocus起作用。显然,这不是我们想要的效果。
使用directive指令实现:
<template>
<div class="hello">
<h2>v-focus</h2>
v-focus : <Input v-model="value1" v-focus placeholder="Enter something..." />
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data() {
return {
value1: '',
}
},
created() {
window.d = this
},
directives: {
focus: {
inserted(el) {
el.focus()
},
}
}
}
</script>
直接操作底层dom获取焦点。无论是刷新页面还是切换路由都是自动获取焦点。
输入框输入韩文时,实时搜索
最后一个字还没输入完,也可以直接搜索
公司项目中,有这方面的改善需求。 使用directive指令实现如下:
<template>
<h2>使用指令</h2>
<Input v-model="value2" v-krSearch placeholder="Enter something..." />
<ul>
<li v-for="item in directiveData">{{item.text}}</li>
</ul>
</template>
<script>
export default {
computed: {
directiveData () {
let list = []
list = _.filter(this.textData, (item, index) => {
if(_.includes(item.text, this.tValue)) {
item.index = index
return true
}
})
return list
}
},
directives: {
krSearch: {
inserted(el,binding,vnode) {
let _this = vnode.context
const tInput = el.querySelector('input[type="text"]')
tInput.addEventListener('input', (e) => {
_this.tValue = e.target.value
})
},
}
},
}
</script>
按钮级别权限控制
公司项目中,有封装过按钮组件
<template>
<div>
<h2>按钮级别权限控制</h2>
<Button type="primary" v-has="'admin'">添加</Button>
<Button type="primary" v-has="'vip'">修改</Button>
<Button type="primary" v-has="'normal'">删除</Button>
<Button type="primary" v-has="'qqq'">取消</Button>
<Button type="primary" v-has>测试</Button>
</div>
</template>
<script>
export default {
name:'',
components: {},
data () {
return {
btnPermissions: ['admin','vip','normal']
};
},
computed: {
},
watch: {
},
created() {
},
mounted() {
},
directives: {
has: {
inserted (el, binding, vnode) {
let _this = vnode.context
let btnPermissionsArr = _this.btnPermissions
if (!_this.has(btnPermissionsArr, binding.value)) {
el.parentNode.removeChild(el)
}
},
}
},
methods: {
has(arr, btnPermissionsStr) {
let isExist = false;
if (btnPermissionsStr == undefined || btnPermissionsStr == null) {
return false;
}
if (arr.indexOf(btnPermissionsStr) > -1) {
isExist = true;
}
return isExist;
}
},
}
</script>
自定义全局指令
以上例子全都是局部指令,当然也可以全局使用。
第一步在main.js中写入我们的全局指令
Vue.directive( 'focus', {
inserted(el) {
el.focus()
},
})
在子组件中,v-focus指令是可以正常使用。
<h2>v-focus</h2>
v-focus : <input v-model="value1" v-focus placeholder="Enter something..." />