vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > vue3.x lodash管理封装指令

vue3.x lodash在项目中管理封装指令的优雅使用

作者:XiaoDaiGua_Ray

这篇文章主要为大家介绍了vue3.x lodash在项目中管理封装指令的优雅使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

vue3.x directives

vue 指令是一种特殊的 vue.js 特性,用于在 DOM 元素上添加特定的行为或功能。指令通过在元素上使用特定的指令名称和参数来实现,可以用于操作 DOM、响应事件、绑定数据等。今天简述一下如何在项目中优雅的管理、封装一些常用的指令。

后面代码基本上会逐行进行注释,以便于大家阅读。

管理指令

统一管理

·
├── 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,可以自行点击查看源码

以上就是vue3.x lodash在项目中管理封装指令的优雅使用的详细内容,更多关于vue3.x lodash管理封装指令的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文