什么是指令
在Vue中提供了一套为数据驱动视图更为方便的操作,这些操作被称为指令系统。我们看到的v-来头的行内属性,都是指令,不同的指令可以完成或者实现不同的功能。
除了核心功能默认内置的指令(v-model和v-show),Vue也允许注册自定义指令
指令使用的几种方式:
//会实例化一个指令,但这个指令没有参数
`v-xxx`// -- 将值传到指令中
`v-xxx="value"` // -- 将字符串传入到指令中,如`v-html="'<p>内容</p>'"`
`v-xxx="'string'"` // -- 传参数(`arg`),如`v-bind:class="className"`
`v-xxx:arg="value"` // -- 使用修饰符(`modifier`)
`v-xxx:arg.modifier="value"`
如何实现
注册一个自定义指令类似于组件注册,分为全局注册和局部注册。
- 全局注册:Vue.directive
- 局部注册:组件directives属性
举例如下:
Vue.directive第一个参数是指令的名字(不需要加上v-前缀),第二个参数可以是一个对象,也可以是一个指令函数
Vue.directive('focus',{inserted:function(el){// 聚焦元素el.focus();//页面加载完成之后自动让输入框获取到焦点的小功能}
})
局部注册通过在组件options选项中设置directive属性
directive:{// 指令的定义focus:{inserted:function(el){el.focus()}}
}
然后可以在模板中任何元素上使用v-focus指令,如下:
<input v-focus />
自定义指令也想组件一样存在钩子函数:
- bind:只调用一次,指令第一次绑定到元素时调用。这里可以进行一次性的初始化设置
- inserted:被绑定的元素插入父节点时调用(仅保证父节点存在,但是不一定已经被插入到文档中)
- update:所有组件VNode更新时调用,但是可能发生在其子VNode更新之前。指令的值可能发生了改变,也可能没有。这里你可以通过比较更新前后的值来忽略不必要的模板更新
- componentUpdate:指令所在组件的VNode及其子VNode全部更新后调用
- unbind:只调用一次,指令与元素解绑时调用
所有钩子函数的参数都有以下:
- el:指令所绑定的元素,可以用来直接操作DOM
- binding:一个对象,包含以下property:
- name:指令名,不包含v-前缀
- value:指令的绑定值,例如:v-my-directive=“1+1”中,绑定值为2。
- oldValue:指令绑定的前一个值, 仅在update和componentUpdate钩子中可用。无论值是否改变都可用。
- 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和componentUpdate钩子可用
注意:除了el之外,其他参数都应该是只读的,切勿进行修改。如果需要再钩子之间共享数据,建议通过元素的dataset来进行。
举个栗子:
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
<script>Vue.directive('demo', function (el, binding) {console.log(binding.value.color) // "white"console.log(binding.value.text) // "hello!"})
</script>
应用场景
使用自定义指令可以满足我们的日常一些场景,这里我列举几个我在项目中用到的自定义指令:
- 表单防止重复提交
- 一键copy的功能
防止表单重复提交
Vue.directive('throttle', {inserted(el, binding) {// 获取指令参数时间,没有则默认赋值为2000let throttleTime = parseInt(binding.value) || 2000el.addEventListener('click', () => {// 第一次执行if (!el.disabled) {// 如果是第一次执行则表示可以进行点击操作el.disabled = truesetTimeout(() => {el.disabled = false}, throttleTime)}})}
})// 2.为button标签设置v-throttle自定义指令
<Button size="large" type="success" v-throttle="5000">新增</Button>
一键复制功能
// 一键复制自定义指令
Vue.directive('copy', {// 选取文本框内容// 执行浏览器复制命令// 复制命令会将当前选中的内容复制到剪切板中(这里就是创建的input标签)// Input要在正常的编辑状态下原生复制方法才会生效inserted (el, binding) {if (!binding.value) {Message.error('请传入需要复制的内容')return}el.addEventListener('click', () => {console.log('binding.value', binding.value) // 模拟输入框let cInput = document.createElement('input')if (binding.value.newAccount === '') {cInput.value = '密码' + ':' + binding.value.newPassword} else {cInput.value = `企业名称:${binding.value.newCompanyName} , 用户名:${binding.value.newWebAccount} , 密码:${binding.value.newPassword};`}document.body.appendChild(cInput);cInput.select();document.execCommand("copy");Message.success('复制成功')document.body.removeChild(cInput);this.dialogVisible = false;// 这个根据自己业务自定义Vue.prototype.$emit("afterComfirm");})},componentUpdated(el, { value }) {el.$value = value;},
})// 为button标签设置v-copy自定义指令
<Button size="large" type="success" v-copy="infoObj">新增</Button>// 其中infoObj如下:
infoObj:{newPassword:this.newPassword,newAccount:this.newAccount...
}// 后面可以扩展将字段为传入的字段,不要写死