共计 5670 个字符,预计需要花费 15 分钟才能阅读完成。
vue3.x directives
vue
指令是一种非凡的 vue.js
个性,用于在 DOM
元素上增加特定的行为或性能。指令通过在元素上应用特定的指令名称和参数来实现,能够用于操作 DOM
、响应事件、绑定数据等。明天简述一下如何在我的项目中优雅的治理、封装一些罕用的指令。
前面代码基本上会逐行进行正文,以便于大家浏览。
治理指令
对立治理
- 对立包治理:通过创立
directives
文件夹,咱们能够将所有与指令相干的文件集中在一个地位,使其更易于查找和保护。这种对立的包治理构造有助于组织和治理指令相干的代码。 - 模块化指令:在
directives/modules
文件夹中,咱们为每个指令创立一个独立的文件夹。这种模块化的构造使每个指令的逻辑和性能被封装在一个独立的文件中,不便独自治理和保护。每个指令文件夹中的进口文件index.ts
能够用于导出指令逻辑,使其在我的项目中得以对立注册和应用。 - 规范化命名:指令文件夹的命名须要长篇累牍,可能清晰表白指令的用处。通过规范化的命名,能够使我的项目中的指令更易于了解和辨认,进步代码的可读性和可维护性。
- 可扩展性:通过这种治理形式,咱们能够轻松地增加新的指令。只需在 directives/modules 文件夹中创立一个新的指令文件夹,并在其中编写相应的指令逻辑即可。这种构造使我的项目具备良好的可扩展性,不便咱们依据须要增加、批改或删除指令。
· | |
├── directives | |
├──├── index.ts | |
├──├── modules | |
├──├──├── some directive | |
├──├──├──├── index.ts type.ts ... |
对立注册
每个指令都应用 index.ts
文件对立裸露一个 CustomDirectiveFC
类型的函数,并且在根目录的 index.ts
中主动收集 modules
文件夹中的所有文件并注册这些指令。
实现
公共类型(type.ts)
import type {Directive} from 'vue' | |
import type {App} from 'vue' | |
export type {DebounceBindingOptions} from './modules/debounce/type' | |
export type {ThrottleBindingOptions} from './modules/throttle/type' | |
export type CustomDirectiveFC<T, K> = () => Directive<T, K> | |
export interface DirectiveModules extends Object {default: CustomDirectiveFC<unknown, unknown>} | |
export type AppType = App<Element> |
收集、注册指令
利用 import.meta.glob
办法(如果是 webpack
则是应用 require.context
),获取所有指令文件夹的门路。而后,遍历这些文件夹,找到蕴含 index.ts
文件的地位。在每个 index.ts
文件中,你能够导入 CustomDirectiveFC
函数。通过这种形式,你能够主动收集所有指令,并进行注册。
正告
该形式会收集modules
中所有的文件夹,并且以文件名称作为指令名称。如果是多个单词,请应用小写单词并以-
连贯。
import type {DirectiveModules, CustomDirectiveFC} from '@/directives/type' | |
export const combineDirective = < | |
T extends Record<string, DirectiveModules>, | |
K extends keyof T, | |
>(directiveModules: T,) => {const directives = Object.keys(directiveModules).reduce((pre, curr) => {const fc = directiveModules[curr]?.default | |
if (typeof fc === 'function') {pre[curr] = fc | |
return pre | |
} else {throw new Error('directiveModules[curr] is not function') | |
} | |
}, {} as Record<K, CustomDirectiveFC<unknown, unknown>>) | |
return directives | |
} |
import {combineDirective} from './helper/combine' | |
import {forIn} from 'lodash-es' | |
import type {App} from 'vue' | |
import type {DirectiveModules} from '@/directives/type' | |
/** | |
* | |
* 初始化全局自定义指令 | |
* | |
* 该办法会将 modules 下每个文件夹视为一个指令 | |
* 并且会将文件夹名称辨认为指令名称 | |
* 每个文件下的 index.ts 文件视为每个指令的入口(也就是指令的解决逻辑, 须要暴露出一个 Directive 类型的对象) | |
*/ | |
export const setupDirectives = (app: App<Element>) => { | |
// 获取 modules 包下所有的 index.ts 文件 | |
const directiveRawModules: Record<string, DirectiveModules> = | |
import.meta.glob('@/directives/modules/**/index.ts', {eager: true,}) | |
// 将所有的包提取进去(./modules/[file-name]/index.ts) | |
const directivesModules = combineDirective(directiveRawModules) | |
// 提取文件名(./modules/copy/index.ts => copy) | |
const regexExtractDirectiveName = /(?<=modules\/).*(?=\/index\.ts)/ | |
// 匹配非法指令名称 | |
const regexDirectiveName = /^([^-]+-)*[^-]+$/ | |
forIn(directivesModules, (value, key) => {const dname = key.match(regexExtractDirectiveName)?.[0] | |
if (typeof dname === 'string' && regexDirectiveName.test(dname)) {app.directive(dname, value?.()) | |
} else {console.error(`[setupDirectives] ${dname} is not a valid directive name`) | |
} | |
}) | |
} |
在 main.ts
文件中导入并且调用 setupDirectives
办法,即可实现自定义指令的收集与注册。而后即可在我的项目全局中应用。
自定义指令
筹备工作咱们曾经实现了,当初只须要开发自定义指令即可。当初咱们来封装几个罕用指令热热手。
v-throttle
实现
import type {ThrottleSettings} from 'lodash-es' | |
import type {AnyFC} from '@/types/modules/utils' | |
export interface ThrottleBindingOptions { | |
func: AnyFC | |
trigger?: string | |
wait?: number | |
options?: ThrottleSettings | |
} |
import {throttle} from 'lodash-es' | |
import {on, off} from '@use-utils/element' | |
import type {ThrottleBindingOptions} from './type' | |
import type {AnyFC} from '@/types/modules/utils' | |
import type {DebouncedFunc} from 'lodash-es' | |
import type {CustomDirectiveFC} from '@/directives/type' | |
const throttleDirective: CustomDirectiveFC< | |
HTMLElement, | |
ThrottleBindingOptions | |
> = () => { | |
let throttleFunction: DebouncedFunc<AnyFC> | null | |
return {beforeMount: (el, { value}) => {const { func, trigger = 'click', wait = 500, options} = value | |
if (typeof func !== 'function') {throw new Error('throttle directive value must be a function') | |
} | |
throttleFunction = throttle(func, wait, Object.assign({}, options)) | |
on(el, trigger, throttleFunction) | |
}, | |
beforeUnmount: (el, { value}) => {const { trigger = 'click'} = value | |
if (throttleFunction) {throttleFunction.cancel() | |
off(el, trigger, throttleFunction) | |
} | |
throttleFunction = null | |
}, | |
} | |
} | |
export default throttleDirective |
应用
<template> | |
<p> 我执行了 {{count}} 次 </p> | |
<p> 该办法 1s 内仅会执行一次 </p> | |
<button | |
v-throttle="{ | |
func: handleClick, | |
trigger: 'click', | |
wait: 1000, | |
options: {},}" | |
> | |
节流按钮 | |
</button> | |
</template> | |
<script setup lang="ts"> | |
const count = ref(0) | |
const handleClick = () => {count.value++} | |
</script> |
v-debounce
实现
import type {DebounceSettings} from 'lodash-es' | |
import type {AnyFC} from '@/types/modules/utils' | |
export interface DebounceBindingOptions { | |
func: AnyFC | |
trigger?: string | |
wait?: number | |
options?: DebounceSettings | |
} |
import {debounce} from 'lodash-es' | |
import {on, off} from '@use-utils/element' | |
import type {DebounceBindingOptions} from './type' | |
import type {AnyFC} from '@/types/modules/utils' | |
import type {DebouncedFunc} from 'lodash-es' | |
import type {CustomDirectiveFC} from '@/directives/type' | |
const debounceDirective: CustomDirectiveFC< | |
HTMLElement, | |
DebounceBindingOptions | |
> = () => { | |
let debounceFunction: DebouncedFunc<AnyFC> | null | |
return {beforeMount: (el, { value}) => {const { func, trigger = 'click', wait = 500, options} = value | |
if (typeof func !== 'function') {throw new Error('debounce directive value must be a function') | |
} | |
debounceFunction = debounce(func, wait, Object.assign({}, options)) | |
on(el, trigger, debounceFunction) | |
}, | |
beforeUnmount: (el, { value}) => {const { trigger = 'click'} = value | |
if (debounceFunction) {debounceFunction.cancel() | |
off(el, trigger, debounceFunction) | |
} | |
debounceFunction = null | |
}, | |
} | |
} | |
export default debounceDirective |
应用
<template> | |
<p> 我执行了 {{count}} 次 </p> | |
<p> 该办法将提早 1s 执行 </p> | |
<button | |
v-throttle="{ | |
func: handleClick, | |
trigger: 'click', | |
wait: 1000, | |
options: {},}" | |
> | |
防抖按钮 | |
</button> | |
</template> | |
<script setup lang="ts"> | |
const count = ref(0) | |
const handleClick = () => {count.value++} | |
</script> |
自定义指令目录构造
· | |
├── directives | |
├──├── index.ts type.ts | |
├──├── modules | |
├──├──├── throttle | |
├──├──├──├── index.ts type.ts | |
├──├──├── debounce | |
├──├──├──├── index.ts type.ts |
依照上述步骤当前,你将会失去这样的一个目录构造。
最初
所有的代码源码都来自于 Ray Template,能够自行点击查看源码。如果感觉对您有帮忙,也能够给模板点一个小星星~~~
正文完