Vue 自定义指令详解
首先,我们知道vue中有很多自带指令,v-bind、v-on、v-model等。但在业务开发中,我们常见一些自定义指令如:v-copy、v-longpress等。那么如何定义自己所需的指令呢?
接下来我们分别从指令注册、指令的钩子函数、指令的参数以及常见指令的封装进行介绍
为什么要自定义指令
在使用vue的时候 我们某些场景下仍然需要对普通 DOM 元素进行底层操作,
在vue中、组件渲染需要时间,获取DOM常需要搭配setTimeout、$nextTick使用
而自定义指令在使用时,更便捷。
指令注册
指令的注册命令:Vue.directive(key, directives[key])
使用:Vue.use()
全局注册
在我们常见的项目结构中、directives文件夹下,定义的index.js文件中,我们会对指令进行全局注册。
import MyDirective from './directive/myDirective' const directives = { MyDirective } export default { install(app) { // 遍历、注册 Object.keys(directives).forEach((key) => { app.directive(key, directives[key]) }) } }
局部注册
在你自己组件或页面中,使用directives选项自定义指令
export default { directives: { myDirective: { inserted: function (el) { // } } } }
使用
<input v-myDirective>
添加参数v-myDirective="data"
传递数值给指令,这里的data可以是组件中的data数据,也可以是methods方法。
<div v-myDirective="{ color: 'white', text: 'hello!' }"></div> app.directive('myDirective', (el, binding) => { console.log(binding.value.color) // => "white" console.log(binding.value.text) // => "hello!" })
v-myDirective:click="clickHandle"
,传递参数click,这里可以通过[xx]的格式动态传递参数。v-myDirective:click.top.bar="clickHandle"
传递修饰符top和bar。
注意:不推荐在组件上使用自定义指令、它会应用于组件的根结点、和透传 attributes 类似。
指令的钩子和参数
const myDirective = { // 在绑定元素的 attribute 前 // 或事件监听器应用前调用 created(el, binding, vnode) { // 下面会介绍各个参数的细节 }, // 在元素被插入到 DOM 前调用 beforeMount(el, binding, vnode) {}, // 在绑定元素的父组件 // 及他自己的所有子节点都挂载完成后调用 mounted(el, binding, vnode) {}, // 绑定元素的父组件更新前调用 beforeUpdate(el, binding, vnode, prevVnode) {}, // 在绑定元素的父组件 // 及他自己的所有子节点都更新后调用 updated(el, binding, vnode, prevVnode) {}, // 绑定元素的父组件卸载前调用 beforeUnmount(el, binding, vnode) {}, // 绑定元素的父组件卸载后调用 unmounted(el, binding, vnode) {} }
- bind: 只调用一次,指令第一次绑定到HTML元素时调用,可以定义一个在绑定时执行一次的初始化动作,此时获取父节点为null。
bind: function (el, {value:{fn,time}}) {}
- el:指令绑定的元素、用来操作DOM
- value:指令的绑定值inserted: 被绑定元素插入父节点时调用,此时可以获取到父节点。
- update: 所在组件的VNode更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新componentUpdated: 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind: 只调用一次, 指令与元素解绑时调用。
常见指令的封装
v-copy
内容复制到剪切板中
const copy = { bind(el, { value }) { el.$value = value el.handler = () => { if (!el.$value) { // 值为空的时候,给出提示。可根据项目UI仔细设计 console.log('无复制内容') return } // 动态创建 textarea 标签 const textarea = document.createElement('textarea') // 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域 textarea.readOnly = 'readonly' textarea.style.position = 'absolute' textarea.style.left = '-9999px' // 将要 copy 的值赋给 textarea 标签的 value 属性 textarea.value = el.$value // 将 textarea 插入到 body 中 document.body.appendChild(textarea) // 选中值并复制 textarea.select() const result = document.execCommand('Copy') if (result) { console.log('复制成功') } document.body.removeChild(textarea) } // 绑定点击事件 el.addEventListener('click', el.handler) }, // 当传进来的值更新的时候触发 componentUpdated(el, { value }) { el.$value = value }, // 指令与元素解绑的时候,移除事件绑定 unbind(el) { el.removeEventListener('click', el.handler) }, } export default copy
v-longpress
实现一个用户长按鼠标左键或移动端单指长按,触发的事件
const longpress = { bind(el, binding) { if (typeof binding.value!== 'function') { throw new Error('Callback must be a function'); } let pressTimer = null; // 鼠标(左键)按下、移动端按下 且 按下持续时间超过 1s 时,触发 const start = (e) => { if ( (e.type === 'mousedown' && e.button!== 0) || (e.type === 'touchstart' && e.touches.length > 1) ) { return; } pressTimer = setTimeout(() => { binding.value(e); }, 1000); }; // 鼠标(左键)抬起、移动端抬起 或 按下时间小于 1s 时,移除定时器 const cancel = () => { if (pressTimer!== null) { clearTimeout(pressTimer); pressTimer = null; } }; // 事件监听 el.addEventListener('mousedown', start); el.addEventListener('touchstart', start); el.addEventListener('click', cancel); el.addEventListener('mouseout', cancel); el.addEventListener('touchend', cancel); el.addEventListener('touchcancel', cancel); }, // 指令与元素解绑的时候,移除事件绑定 unbind(el) { el.removeEventListener('mousedown', start); el.removeEventListener('touchstart', start); el.removeEventListener('click', cancel); el.removeEventListener('mouseout', cancel); el.removeEventListener('touchend', cancel); el.removeEventListener('touchcancel', cancel); }, }; export default longpress
v-waterMarker
页面增加水印
function addWaterMarker(str, parentNode, font, textColor) { // 水印文字,父元素,字体,文字颜色 var can = document.createElement('canvas') parentNode.appendChild(can) can.width = 200 can.height = 150 can.style.display = 'none' var cans = can.getContext('2d') cans.rotate((-20 * Math.PI) / 180) cans.font = font || '16px Microsoft JhengHei' cans.fillStyle = textColor || 'rgba(180, 180, 180, 0.3)' cans.textAlign = 'left' cans.textBaseline = 'Middle' cans.fillText(str, can.width / 10, can.height / 2) parentNode.style.backgroundImage = 'url(' + can.toDataURL('image/png') + ')' } const waterMarker = { bind: function (el, binding) { addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor) }, } export default waterMarker
到此这篇关于Vue 自定义指令的文章就介绍到这了,更多相关Vue 自定义指令内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
vue3 setup语法糖中获取slot插槽的dom对象代码示例
slot元素是一个插槽出口,标示了父元素提供的插槽内容将在哪里被渲染,这篇文章主要给大家介绍了关于vue3 setup语法糖中获取slot插槽的dom对象的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下2024-04-04vue在自定义组件上使用v-model和.sync的方法实例
自定义组件的v-model和.sync修饰符其实本质上都是vue的语法糖,用于实现父子组件的"数据"双向绑定,下面这篇文章主要给大家介绍了关于vue在自定义组件上使用v-model和.sync的相关资料,需要的朋友可以参考下2022-07-07
最新评论