私が最近取り組んでいるプロジェクトでは、Vue のカスタム命令が多く使用されていますが、同僚や友人の間で調査したところ、多くの人が Vue が提供するこの機能をほとんど使用していないことが判明しました。そこで、誰もがカスタム命令の機能を無視してソリューションに別のオプションを追加しないように、私の使用方法をいくつか整理して友人と共有します。
Vue の一連の組み込み命令 (v-model
や v- など) に加えて、 show
) さらに、Vue ではカスタム ディレクティブ (カスタム ディレクティブ) を登録することもできます。
Vue でコードを再利用する 2 つの方法: コンポーネント と 結合関数。コンポーネントは主要な構成要素ですが、合成関数はステートフル ロジックに重点を置いています。一方、カスタム ディレクティブは主に、通常の要素に対する基礎となる DOM アクセスを含むロジックを再利用することを目的としています。
カスタム ディレクティブは、同様のコンポーネントのライフサイクル フックを含むオブジェクトによって定義されます。フック関数は、命令がバインドされている要素をパラメータとして受け取ります。 [関連する推奨事項: vuejs ビデオ チュートリアル 、Web フロントエンド開発 ]
より詳細な紹介については、Vue 公式ドキュメント: cn を参照してください。 vuejs.org/guide/reusa…
Vue カスタム手順には、グローバル登録と ローカル登録の 2 つの方法があります。
まずはグローバル命令の登録方法ですが、Vue.directive(id, [定義])を通じてグローバル命令を登録します。次に、エントリ ファイルで Vue.use() 呼び出しを行います。
バッチ登録手順、新しい src/directives/index.js ファイルを作成します:
import fitColumns from './fit-columns' import enterToInput from './enter-to-input' import resizeHeight from './resize-height' import resizeWidth from './resize-width' import inputFilter from './input-filter' import copy from './copy' import longpress from './longpress' import clickOutside from './click-outside' import emoji from './emoji' const directives = { fitColumns, enterToInput, resizeHeight, resizeWidth, inputFilter, copy, longpress, clickOutside, emoji } export default { install(Vue) { Object.keys(directives).forEach((key) => { Vue.directive(key, directives[key]) }) } }
main.js を導入して呼び出します:
// ..... import Directives from '@/directives' Vue.use(Directives) //.....
次のステップは、特定のカスタム 指示が定義されたので、最初に開発の要点といくつかの開発技術点を詳細に説明する必要があります。
Vue2 バージョン: ディレクティブ定義オブジェクトは、次の フック関数 (すべてオプション) を提供できます:
bind
: 1 回だけ呼び出されます。 、初めてディレクティブが要素にバインドされるとき。ここではワンタイムの初期化設定を行うことができます。 inserted
: バインドされた要素が親ノードに挿入されるときに呼び出されます (親ノードのみが存在することが保証されますが、必ずしもドキュメントに挿入されるわけではありません)。 update
: コンポーネントの VNode が更新されるときに呼び出されます ただし、子 VNode が更新される前に呼び出される場合もあります 。ディレクティブの値は変更されている場合もあれば、変更されていない場合もあります。ただし、更新前後の値を比較することで、不要なテンプレートの更新を無視できます。 componentUpdated
: 命令が配置されているコンポーネントのすべての VNode とそのサブ VNode が更新された後に呼び出されます。 unbind
: 命令が要素からバインドされていないときに、1 回だけ呼び出されます。 コマンド フック関数は、次の parameters で渡されます:
el
: コマンドは次のとおりです。 Elements にバインドされたものを使用して、DOM を直接操作できます。
binding
: 次のプロパティを含むオブジェクト:
name
: 命令名 (# # を除く) #v- プレフィックス。
: 命令のバインディング値。例:
v-my-directive="1 1"、バインディング値は
2# ##。
update
および componentUpdated
フックでのみ使用できます。値が変更されているかどうかに関係なく使用できます。
v-my-directive="1 1"
の場合、式は "1 1"
です。
v-my-directive:foo
では、パラメーターは "foo"
です。
v-my-directive.foo.bar
では、修飾子オブジェクトは { foo: true, bar: true }
です。
oldVnode
:上一个虚拟节点,仅在 update
和 componentUpdated
钩子中可用。
Tips
:除了el
之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的dataset
来进行。
Vue3版本:有稍微变化,由于本文主要说的都是 Vue2 版本的(也是手上很多 vue2 版本的老项目在维护),Vue3的就不做详细介绍了,感兴趣可查看: cn.vuejs.org/guide/reusa…
以下都是我在项目中用到的自定义指令,特此分享出来,供大家参考。同时也不敢保证 100% 无bug,如果在您的使用场景中如有 bug,还望留言批评指导。
1、click-outside.js
场景:clickOutside 自定义指令可以应用于需要在点击元素外部时触发某些操作的场景,例如:
点击外部关闭弹窗:当用户点击弹窗外部时,需要关闭弹窗并执行一些操作,例如清空输入框、重置表单等。
点击外部隐藏下拉菜单:当用户点击下拉菜单外部时,需要隐藏下拉菜单并执行一些操作,例如清空搜索框、重置筛选条件等。
点击外部取消选中状态:当用户点击选中元素外部时,需要取消选中状态并执行一些操作,例如清空选中项、重置状态等。
总之,clickOutside 自定义指令可以帮助我们实现一些常见的交互需求,提升用户体验和操作效率。
const clickOutside = { bind: function(el, binding, vnode) { el.clickOutsideEvent = function(event) { if (!(el === event.target || el.contains(event.target))) { vnode.context[binding.expression](event) } } document.body.addEventListener('click', el.clickOutsideEvent) }, unbind: function(el) { document.body.removeEventListener('click', el.clickOutsideEvent) } } export default clickOutside
2、copy.js
场景: copy 自定义指令可以应用于需要实现一键复制文本内容的场景,例如:
复制分享链接:当用户点击分享按钮时,需要将当前页面的分享链接复制到剪贴板中,方便用户分享给其他人。
复制优惠码:当用户点击领取优惠券按钮时,需要将优惠码复制到剪贴板中,方便用户在购物时使用。
复制代码片段:当用户需要复制代码片段时,可以通过点击复制按钮,将代码片段复制到剪贴板中,方便用户在编辑器中粘贴使用。
总之,copy 自定义指令可以帮助我们实现一些常见的复制操作,提升用户体验和操作效率。
const copy = { bind: function(el, binding) { el.addEventListener('click', function() { const textToCopy = binding.value const input = document.createElement('input') input.setAttribute('value', textToCopy) document.body.appendChild(input) input.select() document.execCommand('copy') document.body.removeChild(input) }) } } export default copy
3、emoji.js
场景: emoji 自定义指令可以应用于需要在输入框中插入表情符号的场景,例如:
发送表情消息:当用户在聊天应用中发送消息时,可以通过点击表情按钮,在输入框中插入表情符号,丰富聊天内容。
评论点赞:当用户在社交应用中对评论进行点赞时,可以通过点击点赞按钮,在评论框中插入点赞表情符号,表达自己的情感。
表情搜索:当用户需要在输入框中插入特定的表情符号时,可以通过输入表情名称或关键字,筛选出符合条件的表情符号,方便用户选择使用。
总之,emoji 自定义指令可以帮助我们实现在输入框中插入表情符号的功能,提升用户体验和操作效率。
// 在指令的inserted钩子函数中,定义一个正则表达式,用来匹配表情及特殊字符。 // 在指令的update钩子函数中,判断输入框的值是否发生变化,如果变化了,则使用正则表达式来过滤输入框的值。 // 在指令的unbind钩子函数中,清除事件监听器,避免内存泄漏。 const emoji = { inserted: function(el) { el.addEventListener('input', function() { const reg = /[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDFFF]|[\u200D\uFE0F\uFE00-\uFE0F]/g el.value = el.value.replace(reg, '') }) }, update: function(el) { const reg = /[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDFFF]|[\u200D\uFE0F\uFE00-\uFE0F]/g el.value = el.value.replace(reg, '') }, unbind: function(el) { el.removeEventListener('input') } } export default emoji
4、enter-to-input.js
场景: enter-to-input 自定义指令可以应用于需要在输入框中按下回车键时触发特定操作的场景,例如:
搜索框回车搜索:当用户在搜索框中输入关键字后,按下回车键时,可以触发搜索操作,快速获取搜索结果。
发送消息:当用户在聊天应用中输入完消息后,按下回车键时,可以触发发送消息操作,方便快捷地发送消息。
提交表单:当用户在表单中填写完信息后,按下回车键时,可以触发提交表单操作,快速提交表单信息。
总之,enter-to-input 自定义指令可以帮助我们实现在输入框中按下回车键时触发特定操作的功能,提升用户体验和操作效率。
const enterToInput = { inserted: function(el) { let inputs = el.querySelectorAll('input') // 绑定回写事件 for (var i = 0; i < inputs.length; i++) { inputs[i].setAttribute('keyFocusIndex', i) inputs[i].addEventListener('keyup', ev => { if (ev.keyCode === 13) { const targetTo = ev.srcElement.getAttribute('keyFocusTo') if (targetTo) { this.$refs[targetTo].$el.focus() } else { var attrIndex = ev.srcElement.getAttribute('keyFocusIndex') var ctlI = parseInt(attrIndex) inputs = el.querySelectorAll('input') if (ctlI < inputs.length - 1) inputs[ctlI + 1].focus() } } }) } } } export default enterToInput
5、fit-columns.js
场景: fit-columns 自定义指令可以应用于需要自动调整表格列宽的场景,例如:
数据展示:当我们需要在页面上展示大量数据时,可以使用表格进行展示,通过 fit-columns 自定义指令可以自动调整表格列宽,使得数据更加清晰易读。
数据编辑:当我们需要在页面上编辑表格数据时,可以使用表格进行编辑,通过 fit-columns 自定义指令可以自动调整表格列宽,使得编辑更加方便快捷。
数据导出:当我们需要将表格数据导出为 Excel 或 CSV 格式时,可以使用表格进行导出,通过 fit-columns 自定义指令可以自动调整表格列宽,使得导出的数据更加美观。
总之,fit-columns 自定义指令可以帮助我们实现自动调整表格列宽的功能,提升数据展示、编辑和导出的效率和美观度。
import './fit-columns.css' function adjustColumnWidth(table, padding = 0) { const colgroup = table.querySelector('colgroup') const colDefs = [...colgroup.querySelectorAll('col')] colDefs.forEach((col) => { const clsName = col.getAttribute('name') const clsWidth = col.getAttribute('width') if (clsWidth < 200) return const cells = [ ...table.querySelectorAll(`td.${clsName}`), ...table.querySelectorAll(`th.${clsName}`) ] if (cells[0] && cells[0].classList && cells[0].classList.contains && cells[0].classList.contains('leave-alone')) { return } const widthList = cells.map((el) => { return el.querySelector('.cell') && el.querySelector('.cell').scrollWidth || 0 }) const max = Math.max(...widthList) table.querySelectorAll(`col[name=${clsName}]`).forEach((el) => { // console.log(222, max + padding) el.setAttribute('width', max + padding > 500 ? 500 : max + padding) }) }) } const fitColumns = { update() { }, bind() { }, inserted(el, binding) { setTimeout(() => { adjustColumnWidth(el, binding.value) }, 300) }, componentUpdated(el, binding) { el.classList.add('r-table') setTimeout(() => { adjustColumnWidth(el, binding.value) }, 300) }, unbind() { } } export default fitColumns
5.1、fit-columns.css
.el-table.r-table .cell { display: inline-block; /* white-space: nowrap; */ width: auto; overflow: auto; } .el-table.r-table .el-table__body-wrapper { overflow-x: auto; }
6、input-filter.js
场景: input-filter 自定义指令可以应用于需要对用户输入进行过滤和限制的场景,例如:
输入框过滤:当我们需要在输入框中输入特定类型的数据时,可以使用 input-filter 自定义指令对用户输入进行过滤和限制,例如只允许输入数字、字母或特定字符等。
表单验证:当我们需要对表单中的数据进行验证时,可以使用 input-filter 自定义指令对用户输入进行过滤和限制,例如验证手机号码、邮箱地址等。
密码输入:当我们需要用户输入密码时,可以使用 input-filter 自定义指令对用户输入进行过滤和限制,例如限制密码长度、只允许输入特定字符等。
总之,input-filter 自定义指令可以帮助我们实现对用户输入进行过滤和限制的功能,提升表单验证和数据输入的效率和准确性。
const findEle = (parent, type) => { return parent.tagName.toLowerCase() === type ? parent : parent.querySelector(type) } const trigger = (el, type) => { const e = document.createEvent('HTMLEvents') e.initEvent(type, true, true) el.dispatchEvent(e) } const inputFilter = { mounted(el, binding, vnode) { const bindV = binding.value const regRule = bindV.regRule ? bindV.regRule : /[^\a-zA-Z0-9\u4E00-\u9FA5]+$/g const length = bindV.length ? bindV.length : 30 const $inp = findEle(el, 'input') el.$inp = $inp $inp.handle = () => { const val = $inp.value $inp.value = val.replace(regRule, '').substring(0, length) trigger($inp, 'input') } $inp.addEventListener('keyup', $inp.handle) }, unmounted(el) { el.$inp.removeEventListener('keyup', el.$inp.handle) } } export default inputFilter
7、longpress.js
场景: longpress 自定义指令可以应用于需要长按触发事件的场景,例如:
按钮长按:当我们需要在按钮上长按触发某个事件时,可以使用 longpress 自定义指令,例如长按删除按钮可以删除某个元素。 图片预览:当我们需要在图片上长按触发预览事件时,可以使用 longpress 自定义指令,例如长按图片可以弹出预览框。 列表操作:当我们需要在列表中长按触发某个操作时,可以使用 longpress 自定义指令,例如长按列表项可以弹出操作菜单。
总之,longpress 自定义指令可以帮助我们实现长按触发事件的功能,提升用户体验和操作效率。
// 在 bind 钩子函数中绑定了 mousedown、touchstart、click、mouseout、touchend 和 touchcancel 事件。 // 当用户按下鼠标或触摸屏时,我们会启动一个定时器,如果在指定的时间内没有松开鼠标或手指,则执行指令的回调函数。 // 如果用户在指定的时间内松开了鼠标或手指,则取消定时器。 const longpress = { bind: function(el, binding) { let pressTimer = null const duration = binding.value || 500 // 默认长按时间为 500ms const start = function(event) { if (event.type === 'click' && event.button !== 0) { return } if (pressTimer === null) { pressTimer = setTimeout(() => { handler() }, duration) } } const cancel = function() { if (pressTimer !== null) { clearTimeout(pressTimer) pressTimer = null } } const handler = function() { binding.value() } el.addEventListener('mousedown', start) el.addEventListener('touchstart', start) el.addEventListener('click', cancel) el.addEventListener('mouseout', cancel) el.addEventListener('touchend', cancel) el.addEventListener('touchcancel', cancel) } } export default longpress
8、resize-height.js
场景: resize-height 自定义指令可以应用于需要根据内容自适应高度的场景,例如:
文本框自适应高度:当我们需要在文本框中输入多行文本时,可以使用 resize-height 自定义指令,使文本框根据内容自适应高度,避免内容溢出或留白。
评论框自适应高度:当我们需要在评论框中输入多行文本时,可以使用 resize-height 自定义指令,使评论框根据内容自适应高度,提升用户体验和操作效率。
动态列表自适应高度:当我们需要在动态列表中展示不同高度的内容时,可以使用 resize-height 自定义指令,使列表项根据内容自适应高度,避免内容溢出或留白。
总之,resize-height 自定义指令可以帮助我们实现根据内容自适应高度的功能,提升用户体验和界面美观度。
const resizeHeight = { // 绑定时调用 bind(el, binding) { let height = '' function isResize() { // 可根据需求,调整内部代码,利用 binding.value 返回即可 const style = document.defaultView.getComputedStyle(el) if (height !== style.height) { // 此处关键代码,通过此处代码将数据进行返回,从而做到自适应 binding.value({ height: style.height }) } height = style.height } // 设置调用函数的延时,间隔过短会消耗过多资源 el.__vueSetInterval__ = setInterval(isResize, 100) }, unbind(el) { clearInterval(el.__vueSetInterval__) } } export default resizeHeight
9、resize-width.js
场景: resize-width 自定义指令可以应用于需要根据内容自适应宽度的场景,例如:
图片自适应宽度:当我们需要在页面中展示不同宽度的图片时,可以使用 resize-width 自定义指令,使图片根据内容自适应宽度,避免图片变形或溢出。
表格自适应宽度:当我们需要在页面中展示不同宽度的表格时,可以使用 resize-width 自定义指令,使表格根据内容自适应宽度,避免表格变形或溢出。
动态列表自适应宽度:当我们需要在动态列表中展示不同宽度的内容时,可以使用 resize-width 自定义指令,使列表项根据内容自适应宽度,避免内容变形或溢出。
总之,resize-width 自定义指令可以帮助我们实现根据内容自适应宽度的功能,提升用户体验和界面美观度。
const resizeWidth = { // 绑定时调用 bind(el, binding) { let width = '' function isResize() { // 可根据需求,调整内部代码,利用binding.value返回即可 const style = document.defaultView.getComputedStyle(el) if (width !== style.width) { // 此处关键代码,通过此处代码将数据进行返回,从而做到自适应 binding.value({ width: style.width }) } width = style.width } // 设置调用函数的延时,间隔过短会消耗过多资源 el.__vueSetInterval__ = setInterval(isResize, 100) }, unbind(el) { clearInterval(el.__vueSetInterval__) } } export default resizeWidth
Vue2 自定义指令的原理可以简单概括为:通过 Vue.directive() 方法注册指令,当指令被绑定到元素上时,Vue 会创建一个指令实例,该实例包含指令的钩子函数和其他配置项,然后根据指令的生命周期钩子函数,依次执行相应的逻辑,例如 bind、inserted、update、componentUpdated 和 unbind 等。
具体来说,Vue2 自定义指令的原理包括以下几个方面:
注册指令:通过 Vue.directive() 方法注册指令,该方法接收两个参数,第一个参数是指令名称,第二个参数是一个对象,包含指令的钩子函数和其他配置项。
创建指令实例:当指令被绑定到元素上时,Vue 会创建一个指令实例,该实例包含指令的钩子函数和其他配置项。
指令钩子函数执行:当指令实例被创建后,Vue 会根据指令的生命周期钩子函数,依次执行相应的逻辑,例如 bind、inserted、update、componentUpdated 和 unbind 等。
操作 DOM:在指令钩子函数中,我们可以通过 el 参数获取到指令绑定的元素,然后对元素进行操作,例如修改元素的样式、属性或内容等。
注销指令:当指令被解绑或元素被销毁时,Vue 会调用指令的 unbind 钩子函数,我们可以在该函数中清除指令创建的事件监听器、定时器或其他资源。
总之,Vue2 自定义指令的原理是通过注册指令、创建指令实例、执行指令钩子函数、操作 DOM 和注销指令等步骤来实现的,通过这些步骤,我们可以实现各种自定义指令的功能。
最后,就是想通过这一系列自定义指令的使用,理解 Vue 开放这个 API 的意义,以及我们使用的意义。
Vue 使用自定义指令的意义在于可以扩展 Vue 的功能,让我们可以通过自定义指令来实现一些特定的需求,例如:
操作 DOM:通过自定义指令,我们可以直接操作 DOM 元素,例如修改元素的样式、属性或内容等。
封装组件:通过自定义指令,我们可以封装一些常用的组件功能,例如滚动加载、拖拽排序、表单验证等,使得我们可以在多个组件中复用这些功能。
提高可读性:通过自定义指令,我们可以将一些常用的操作封装成指令,使得代码更加简洁易懂,提高了代码的可读性。
解耦逻辑:通过自定义指令,我们可以将一些逻辑与组件解耦,使得组件更加专注于视图的渲染,而逻辑则由指令来处理。
总之,Vue 使用自定义指令的意义在于可以扩展 Vue 的功能,使得我们可以更加方便地实现一些特定的需求,提高了代码的可读性和可维护性。
以上がvue でのカスタム命令の使用に関する簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。