1. 概述
除了核心功能默认内置的指令,Vue也允许注册自定义指令。有的情况下,对普通 DOM 元素进行底层操作,这时候就会用到自定义指令绑定到元素上执行相关操作。
自定义指令分为:
全局指令和局部指令,当全局指令和局部指令同名时以局部指令为准。
局部指令:只对当前实例(或组件)生效
全局指令:对全部实例(或组件)都生效
2. 钩子函数
自定义指令常用钩子函数:
- bind 第一次绑定到元素时调用(初始化)
- inserted 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
- update 数据更新时调用
- componentUpdated 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
- unbind 只调用一次,指令与元素解绑时调用。
指令的钩子会传递以下几种参数:
el
:指令绑定到的元素。这可以用于直接操作 DOM。
binding
:一个对象,包含以下属性。
value
:传递给指令的值。例如在v-my-directive="1 + 1"
中,值是 2。oldValue
:之前的值,仅在beforeUpdate
和updated
中可用。无论值是否更改,它都可用。arg
:传递给指令的参数 (如果有的话)。例如在v-my-directive:foo
中,参数是"foo"
。modifiers
:一个包含修饰符的对象 (如果有的话)。例如在v-my-directive.foo.bar
中,修饰符对象是{ foo: true, bar: true }
。instance
:使用该指令的组件实例。dir
:指令的定义对象。
vnode
:代表绑定元素的底层 VNode。
prevNode
:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate
和 updated
钩子中可用。
钩子函数可以理解为一个类,类中的构造函数绑定了5个函数(即钩子函数),当我们自定义钩子函数时,就会初始化这个类,然后让我们的相关代码按顺序执行这5个函数。
有关钩子函数更详细的解释,下面这篇文章中的前两个教程写得很通俗易懂,特此推荐:
钩子函数的执行顺序:
<div id="app1"> <div v-red v-if="isShow"> <input type="text" v-model="title"> </div> </div> <script> Vue.directive('red', { // bind 第一次绑定到元素时调用 bind(el, bindings) { console.log('bind'); }, // inserted inserted(el, bindings) { console.log('inserted'); }, // update update(el, bindings) { console.log('update'); }, // componentUpdate componentUpdated(el, bindings) { console.log('componentUpdated'); }, // unbind unbind(el, bindings) { console.log('unbind'); }, }) const vm1 = new Vue({ el: '#app1', data: { isShow: true, title: '钩子函数执行顺序' } }) </script>
程序一执行,当数据源中的数据第一次绑定了元素就会执行bind
函数,当绑定元素插入到父元素中,即显示到视图中时,会执行inserted
函数:
当我们改变视图,使得数据发生改变时,就会执行update
和componentUpdated
函数:
当我们销毁被绑定元素时,即被绑定元素和数据源解绑,就会触发unbind
函数:
3. 自定义全局指令
描述:
全局定义的指令,所有的组件或vue的实例都会生效。
语法:
Vue.directive('指令名称,不需要写v-开头',对象或函数)
Vue.directive('test',{ bind(el,bind){ console.log(el) } })
案例:
<div id="app1"> <div v-red>{{title}}</div> </div> <div id="app2"> <div v-red>标题2</div> </div> <script> //自定义全局指令 Vue.directive('red', { // bind 第一次绑定到元素时调用 bind(el, bindings) { el.style.cssText = `color:red;font-size:30px` } }) const vm1 = new Vue({ el: '#app1', data: { isShow: true, title: '标题1' } }) const vm2 = new Vue({ el: '#app2', data: { } }) </script>
4. 自定义局部指令
描述:
定义局部指令,只有当前的实例能用。
语法:
new Vue({ directives: { test:{ bind(el,bind){} }, // bind/update test2(el,bind){} } })
案例:
<div id="app1"> <div v-red>{{title}}</div> </div> <div id="app2"> <div v-red>标题2</div> </div> <script> const vm1 = new Vue({ el: '#app1', data: { isShow: true, title: '标题1' }, // 定义局部指令,只有当前的实例能用 directives: { red: { // bind它还没有绑定到父元素中,初始化 bind(el) { el.style.cssText = `color:red;font-size:30px` } } } }) const vm2 = new Vue({ el: '#app2', data: { }, directives: { red: { bind(el) { el.style.cssText = `color:blue;font-size:30px` } } } }) </script>
利用自定义局部指令操作Dom:
<div id="app1"> <div v-red>{{title}}</div> </div> <div id="app2"> <div v-red>标题2</div> </div> <script> const vm1 = new Vue({ el: '#app1', data: { isShow: true, title: '标题1' }, directives: { red: { // bind表示被绑定元素还没有插入到父元素中 bind(el) { el.style.cssText = `color:red;font-size:30px` const divDom = document.createElement('div') divDom.innerHTML = '我是标题1的孩子' el.appendChild(divDom) }, // 这时被绑定元素已经插入到父元素中去了,所以可以打印出被绑定元元素的父节点 inserted(el) { console.log(el.parentNode); } } } }) const vm2 = new Vue({ el: '#app2', data: { }, directives: { red: { bind(el) { el.style.cssText = `color:blue;font-size:30px` } } } }) </script>
注意:在上面的代码中,我们在bind
函数中,不能获取当前被绑定元素的父节点,因为此时被绑定元素刚刚初始化,还没有插入到父节点当中。在inserted
函数中才能获取被绑定元素的父节点,因为此时元素已经插入到父节点当中去了。
5. 使用自定义指令实现权限管理
目标:
根据地址栏中的数据,决定是否显示 button 按钮。如果地址栏中的 username 的值是 admin 时,就显示 button 按钮,否则不显示。
注意:目标中的显示与不显示,是取决于该 Dom 元素(button 按钮)是否存在,而不是通过 css 来进行显示与隐藏。
代码:
<div id="app1"> <button v-auth>查看工资</button> </div> <script> const vm1 = new Vue({ el: '#app1', data: { isShow: true, title: '标题1' }, directives: { auth: { // 注意:是否删除按钮的操作,必须在被绑定元素(即当前元素)已经插入父节点(插入到视图)当中后进行 inserted(el) { // 这是一种比较粗暴的写法,可维护性较低 // if (location.search != '?username=admin') { // el.remove() // } // URLSearchParams它是html5提供的新的Api方法,用于获取url地址中的search转为对象 let urlSearch = new URLSearchParams(location.search) // 这样的写法可维护性较高,假如显示元素的权限还需要给到 张三 用户,则直接修改判断条件中的表达式即可 if (urlSearch.get('username') != 'admin') { // 以前兼容性更好的写法,但是现在可以不管 // el.parentNode.removeChild(el) el.remove() } } } } }) </script>
6. 使用自定义指令实现表单验证
首先我们先完成验证手机号的功能。
目标:
在初始化(在 bind 函数中进行)和更新数据(在 update 函数中进行)时都要进行手机号的验证。
思路:
先获取手机号(收集数据),再用正则表达式判断输入框中的手机号是否合法,如果合法则手机号显示黑色,如果不合法则手机号显示红色。
代码:
<div id="app"> <div> <input type="text" v-phone="phone" v-model="phone"> </div> </div> <script> Vue.directive('phone', { // 方法1:直接通过dom来完成数据的收集 // update(el) { // console.log(el.value); // } // 方法2:可以通过传值的方式(钩子函数)完成数据收集 bind(el, { value }) { // 手机号码 let reg = /^1[3-9]\d{9}$/ if (!reg.test(value)) { // 不合法 el.style.color = 'red' } else { el.style.color = 'black' } }, update(el, { value }) { // 手机号码 let reg = /^1[3-9]\d{9}$/ if (!reg.test(value)) { // 不合法 el.style.color = 'red' } else { el.style.color = 'black' } } }) const vm1 = new Vue({ el: '#app', data: { phone: '13525125121', } }) </script>
在上面代码中,通过钩子函数完成数据获取与验证,代码重复率高,所以在钩子函数部分,我们可以简写成下面这种方式:
Vue.directive('phone', (el, { value }) => { // 手机号码 let reg = /^1[3-9]\d{9}$/ if (!reg.test(phone)) { // 不合法 el.style.color = 'red' } else { el.style.color = 'black' } })
注意:简写方式就是自定义指令语法中,第二个参数是函数的写法。自定义指令的简写,指的是将 bind 函数和 update 函数封装起来的写法。
在上面的基础上,我们再加上验证错误信息的显示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>vue学习使用</title> <script src="https://www.atool.online/article/js/vue.js"></script> </head> <body> <div id="app"> <div> <input type="text" v-phone="phone" v-model="phone"> </div> </div> <script> Vue.directive('phone', (el, { value }) => { // 手机号码 let reg = /^1[3-9]\d{9}$/ if (!reg.test(value)) { // 不合法 el.style.color = 'red' // 没有 span 标签时,就创建 span 标签 if (!el.nextSibling) { const spanDom = document.createElement('span') spanDom.innerHTML = '不合法,修改一下' // 这里是防止初始化时数据就不合法,导致被绑定元素不能成功插入到父结点中 // el.parentNode?.appendChild(spanDom) // 上面的写法要求的浏览器版本较高,下面的写法兼容性更好 el.parentNode && el.parentNode.appendChild(spanDom) } } else {// 输入正确时,移除 span 标签 el.style.color = 'black' el.nextSibling && el.nextSibling.remove() } }) const vm1 = new Vue({ el: '#app', data: { phone: '13525125121', phoneMsg: '' } }) </script> </body> </html>
最后,我们使用模块化的的思路,将案例完善一下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>vue学习使用</title> <script src="https://www.atool.online/article/js/vue.js"></script> </head> <body> <div id="app"> <div> <!-- 这种写法的 value 值直接就是 phone --> <!-- <input type="text" v-validate="phone" v-model="phone"> --> <input type="text" v-validate.phone v-model="phone"> </div> <div> <input type="text" v-validate.email v-model="email"> </div> <div> <!-- "{len:3,msg:'长度过长'}":v-validate指令的value值 --> <input type="text" v-validate.str="{len:3,msg:'长度过长'}" v-model="str"> </div> </div> <script> // 在对象里存方法 const validateMethod = { phone(el) { // 手机号码 let reg = /^1[3-9]\d{9}$/ if (!reg.test(el.value)) { // 不合法 el.style.color = 'red' if (!el.nextSibling) { const spanDom = document.createElement('span') spanDom.innerHTML = '不合法,修改一下' // el.parentNode?.appendChild(spanDom) el.parentNode && el.parentNode.appendChild(spanDom) } } else { el.style.color = 'black' el.nextSibling && el.nextSibling.remove() } console.log('phone'); }, email(el, value) { console.log('email') }, str(el, value) { // 这里这个判断是容错处理,也可以不写。因为我们上面的代码中给 value传值了 // 如果当前封装好的代码(当作组件)给别人使用的话,使用者可能不穿值 if (value) { if (el.value.length > value.len) { if (!el.nextSibling) { const spanDom = document.createElement('span') spanDom.innerHTML = value.msg // 防止初始化数据不合法,父节点不存在 el.parentNode?.appendChild(spanDom) } } else { el.nextSibling?.remove() } } } } Vue.directive('validate', (el, { value, modifiers }) => { // modifiers:一个包含修饰符的对象 (如果有的话)。 // console.log(Object.keys(modifiers))===phone Object.keys(modifiers).forEach(name => { // 调用对象中的方法 validateMethod[name](el, value) }) }) const vm1 = new Vue({ el: '#app', data: { phone: '13525125121', email: 'aa@aa.com', str: 'aaa' }, methods: { } }) </script> </body> </html>
注意:
v-validate="phone"
的 value 值是数据源中的 phone,v-validate.phone
的 value 值是 undefined,v-validate.str="{len:3,msg:'长度过长'}"
的 value 值是{len:3,msg:'长度过长'}
。
到此这篇关于Vue自定义指令的使用详细介绍的文章就介绍到这了,更多相关Vue自定义指令内容请搜索阿兔在线工具以前的文章或继续浏览下面的相关文章希望大家以后多多支持阿兔在线工具!