Skip to content

工具函数概览

Vue3 + TypeScript 项目的完整工具函数库,提供了丰富的实用工具函数,涵盖了前端开发中的各种常见需求。本文档详细介绍所有工具函数的功能、参数和使用方法。

工具函数库架构

工具函数库采用模块化设计,按功能分类组织代码,每个模块专注于特定领域的功能实现:

src/utils/
├── string.ts          # 字符串处理工具
├── object.ts          # 对象操作工具
├── date.ts            # 日期时间工具
├── format.ts          # 数据格式化工具
├── function.ts        # 函数增强工具
├── validators.ts      # 数据验证工具
├── boolean.ts         # 布尔值处理工具
├── crypto.ts          # 加密工具
├── rsa.ts             # RSA 加密工具
├── cache.ts           # 浏览器缓存工具
├── class.ts           # CSS 类名操作工具
├── scroll.ts          # 滚动控制工具
├── tree.ts            # 树形数据工具
├── modal.ts           # UI 弹窗工具
├── tab.ts             # 标签页管理工具
└── to.ts              # 异步错误处理工具

字符串处理 string.ts

提供全面的字符串处理功能,包括基本操作、格式化、URL 处理、验证和转换。

函数列表

函数名说明参数返回值
parseStrEmpty空值转换str: string | null | undefinedstring
isEmpty判断字符串是否为空str: anyboolean
isNotEmpty判断字符串是否非空str: anyboolean
capitalize首字母大写str: stringstring
truncate字符串截断str: string, length: number, suffix?: stringstring
byteLength计算字节长度str: stringnumber
createUniqueString生成唯一字符串-string
sprintf格式化字符串str: string, ...args: any[]string
html2TextHTML 转纯文本html: stringstring
getTextExcerpt获取文本摘要text: string, maxLength: numberstring
escapeHtml转义 HTML 特殊字符text: stringstring
isExternal判断是否为外部链接path: stringboolean
isHttp判断是否为 HTTP URLurl: stringboolean
getQueryObject解析 URL 查询参数url: stringobject
objectToQuery对象转查询字符串obj: objectstring
normalizePath路径标准化path: stringstring
isPathMatch路径匹配检查path: string, pattern: stringboolean
camelToKebab驼峰转短横线str: stringstring
kebabToCamel短横线转驼峰str: stringstring
isValidJSONJSON 格式验证str: stringboolean

使用示例

typescript
import {
  parseStrEmpty,
  capitalize,
  truncate,
  isExternal,
  camelToKebab,
  sprintf,
  html2Text,
  getQueryObject,
  isValidJSON,
  byteLength
} from '@/utils/string'

// 空值转换 - 将 null/undefined 转为空字符串
const value1 = parseStrEmpty(null)       // ''
const value2 = parseStrEmpty(undefined)  // ''
const value3 = parseStrEmpty('hello')    // 'hello'

// 首字母大写
const title = capitalize('hello world')   // 'Hello world'
const name = capitalize('vue framework')  // 'Vue framework'

// 字符串截断 - 超出长度添加省略号
const short = truncate('这是一个很长的文本内容', 10)      // '这是一个很长...'
const custom = truncate('Hello World', 8, '***')          // 'Hello***'

// 判断外部链接
const isExt1 = isExternal('https://example.com')   // true
const isExt2 = isExternal('http://localhost:8080') // true
const isExt3 = isExternal('/dashboard')            // false
const isExt4 = isExternal('mailto:test@test.com')  // true

// 命名格式转换
const kebab = camelToKebab('backgroundColor')  // 'background-color'
const camel = kebabToCamel('font-size')        // 'fontSize'

// 格式化字符串(类似 C 语言 sprintf)
const msg = sprintf('用户 %s 已登录,ID: %d', '张三', 12345)
// '用户 张三 已登录,ID: 12345'

// HTML 转纯文本
const text = html2Text('<p>Hello <strong>World</strong></p>')
// 'Hello World'

// 解析 URL 查询参数
const params = getQueryObject('https://example.com?name=test&id=123')
// { name: 'test', id: '123' }

// JSON 格式验证
const valid1 = isValidJSON('{"name":"test"}')  // true
const valid2 = isValidJSON('{invalid}')         // false

// 计算字节长度(中文占 3 字节)
const len1 = byteLength('Hello')    // 5
const len2 = byteLength('你好')     // 6
const len3 = byteLength('Hello你好') // 11

技术实现细节

parseStrEmpty 实现原理

typescript
export const parseStrEmpty = (str: string | null | undefined): string => {
  if (!str || str === 'undefined' || str === 'null') {
    return ''
  }
  return str
}

isExternal 正则匹配

typescript
export const isExternal = (path: string): boolean => {
  return /^(https?:|mailto:|tel:|\/\/)/.test(path)
}

byteLength 实现

typescript
export const byteLength = (str: string): number => {
  let len = 0
  for (let i = 0; i < str.length; i++) {
    if (str.charCodeAt(i) > 127 || str.charCodeAt(i) === 94) {
      len += 3  // 中文等宽字符占 3 字节
    } else {
      len += 1  // ASCII 字符占 1 字节
    }
  }
  return len
}

对象操作 object.ts

提供强大的对象和数组处理工具,包括深度操作、属性访问、数据清理和格式转换。

函数列表

函数名说明参数返回值
isEmptyObject判断是否为空对象obj: objectboolean
shallowEqual浅比较两个对象obj1: object, obj2: objectboolean
objectMerge深度合并对象target: object, source: objectobject
deepClone深拷贝对象source: anyany
getPropertyByPath按路径获取属性值obj: object, path: stringany
get安全获取嵌套属性obj: object, path: string, defaultValue?: anyany
set设置嵌套属性值obj: object, path: string, value: anyvoid
pick选取指定属性obj: object, keys: string[]object
omit排除指定属性obj: object, keys: string[]object
removeEmpty移除空值属性obj: objectobject
cleanArray清理数组空值arr: any[]any[]
uniqueArr数组去重arr: any[]any[]
groupBy数组分组arr: any[], key: stringobject
queryToObject查询字符串转对象query: stringobject
objectToQuery对象转查询字符串obj: objectstring
camelizeKeys键名转驼峰obj: objectobject
snakeizeKeys键名转下划线obj: objectobject

使用示例

typescript
import {
  deepClone,
  get,
  set,
  pick,
  omit,
  removeEmpty,
  groupBy,
  objectMerge,
  shallowEqual,
  uniqueArr,
  camelizeKeys,
  snakeizeKeys
} from '@/utils/object'

// 深拷贝对象 - 创建完全独立的副本
const original = {
  user: { name: '张三', age: 25 },
  tags: ['开发', '设计']
}
const copy = deepClone(original)
copy.user.name = '李四'  // 不影响 original

// 安全获取嵌套属性 - 避免 undefined 错误
const user = {
  profile: {
    name: '张三',
    address: { city: '北京' }
  }
}
const name = get(user, 'profile.name', '匿名')           // '张三'
const city = get(user, 'profile.address.city', '未知')  // '北京'
const country = get(user, 'profile.address.country', '中国')  // '中国'(使用默认值)
const phone = get(user, 'contact.phone', '')  // ''(路径不存在使用默认值)

// 设置嵌套属性值
const data = {}
set(data, 'user.profile.name', '张三')
// data = { user: { profile: { name: '张三' } } }

// 选取指定属性
const fullUser = { id: 1, name: '张三', email: 'test@test.com', password: '123456' }
const safeUser = pick(fullUser, ['id', 'name', 'email'])
// { id: 1, name: '张三', email: 'test@test.com' }

// 排除指定属性
const publicUser = omit(fullUser, ['password'])
// { id: 1, name: '张三', email: 'test@test.com' }

// 移除空值属性
const formData = { name: '张三', age: null, email: '', phone: undefined }
const cleanData = removeEmpty(formData)
// { name: '张三' }

// 数组分组
const users = [
  { name: '张三', dept: '研发部' },
  { name: '李四', dept: '销售部' },
  { name: '王五', dept: '研发部' }
]
const grouped = groupBy(users, 'dept')
// { '研发部': [{...}, {...}], '销售部': [{...}] }

// 深度合并对象
const defaults = { theme: 'light', fontSize: 14, sidebar: { collapsed: false } }
const userConfig = { theme: 'dark', sidebar: { width: 200 } }
const merged = objectMerge(defaults, userConfig)
// { theme: 'dark', fontSize: 14, sidebar: { collapsed: false, width: 200 } }

// 浅比较
const obj1 = { a: 1, b: 2 }
const obj2 = { a: 1, b: 2 }
const obj3 = { a: 1, b: 3 }
shallowEqual(obj1, obj2)  // true
shallowEqual(obj1, obj3)  // false

// 数组去重
const arr = [1, 2, 2, 3, 3, 3]
const unique = uniqueArr(arr)  // [1, 2, 3]

// 键名格式转换
const snakeObj = { user_name: '张三', user_age: 25 }
const camelObj = camelizeKeys(snakeObj)  // { userName: '张三', userAge: 25 }

const camelData = { userName: '张三', userAge: 25 }
const snakeData = snakeizeKeys(camelData)  // { user_name: '张三', user_age: 25 }

技术实现细节

get 函数实现

typescript
export const get = (obj: any, path: string, defaultValue: any = undefined): any => {
  if (!obj || !path) return defaultValue

  const keys = path.split('.')
  let result = obj

  for (const key of keys) {
    if (result === null || result === undefined) {
      return defaultValue
    }
    result = result[key]
  }

  return result === undefined ? defaultValue : result
}

deepClone 实现

typescript
export const deepClone = <T>(source: T): T => {
  if (source === null || typeof source !== 'object') {
    return source
  }

  if (source instanceof Date) {
    return new Date(source.getTime()) as any
  }

  if (source instanceof Array) {
    return source.map(item => deepClone(item)) as any
  }

  if (source instanceof Object) {
    const copy = {} as T
    Object.keys(source).forEach(key => {
      copy[key as keyof T] = deepClone((source as any)[key])
    })
    return copy
  }

  return source
}

日期时间 date.ts

提供完整的日期时间处理方案,包括格式化、解析、计算和范围获取。自动兼容 yyyy-MM-ddYYYY-MM-DD 两种格式语法。

函数列表

函数名说明参数返回值
formatDate日期格式化time: Date | string | number, pattern?: stringstring
formatTableDate表格时间格式化cellValue: string, pattern?: stringstring
formatDay日期格式化(仅年月日)time: Date | string | numberstring
formatRelativeTime相对时间格式化time: string | number, option?: stringstring
formatDateRange日期范围格式化dateRange: [Date, Date], separator?: string, format?: stringstring
parseDate解析日期字符串dateStr: stringDate | null
getCurrentTime获取当前时间pattern?: stringstring
getCurrentDate获取当前日期-string
getCurrentDateTime获取当前日期时间-string
getTimeStamp获取时间戳type?: 'ms' | 's'number
getDateRange获取日期范围days: number[Date, Date]
getCurrentWeekRange获取本周范围-[Date, Date]
getCurrentMonthRange获取本月范围-[Date, Date]
addDateRange添加日期范围参数params: any, dateRange: any[], propName?: stringany
getDateRangeByType按类型获取日期范围dateType: string[string, string] | null
initDateRangeFromQuery从路由初始化日期范围query: object, dateParamName?: string[string, string]
getDaysBetween计算日期间隔天数start: Date, end: Datenumber
isSameDay判断是否同一天date1: Date, date2: Dateboolean
getWeekOfYear获取年内周数date: Datenumber
dateAdd日期加减date: Date, type: string, value: numberDate

使用示例

typescript
import {
  formatDate,
  formatDay,
  formatRelativeTime,
  getCurrentDateTime,
  getCurrentDate,
  getDaysBetween,
  getDateRangeByType,
  getCurrentWeekRange,
  getCurrentMonthRange,
  dateAdd,
  addDateRange
} from '@/utils/date'

// 日期格式化 - 支持多种输入类型
const now = new Date()
const dateStr1 = formatDate(now, 'yyyy-MM-dd HH:mm:ss')  // '2025-03-29 15:30:45'
const dateStr2 = formatDate(now, 'YYYY-MM-DD')            // '2025-03-29'
const dateStr3 = formatDate(now, 'yyyy年MM月dd日')        // '2025年03月29日'
const dateStr4 = formatDate(now, 'HH:mm')                 // '15:30'

// 从时间戳格式化
const timestamp = 1711698645000
const dateStr5 = formatDate(timestamp, 'yyyy-MM-dd')  // '2025-03-29'

// 从字符串格式化
const dateStr6 = formatDate('2025-03-29T15:30:45', 'yyyy年MM月dd日 HH时mm分')
// '2025年03月29日 15时30分'

// 仅格式化年月日
const day = formatDay(now)  // '2025-03-29'

// 相对时间格式化
const relTime1 = formatRelativeTime(Date.now() - 1000 * 30)    // '刚刚'
const relTime2 = formatRelativeTime(Date.now() - 1000 * 60 * 5)  // '5分钟前'
const relTime3 = formatRelativeTime(Date.now() - 1000 * 60 * 60 * 3)  // '3小时前'
const relTime4 = formatRelativeTime(Date.now() - 1000 * 60 * 60 * 25)  // '1天前'

// 获取当前时间
const currentTime = getCurrentDateTime()  // '2025-03-29 15:30:45'
const currentDate = getCurrentDate()      // '2025-03-29'

// 计算日期差
const startDate = new Date('2025-01-01')
const endDate = new Date('2025-03-29')
const days = getDaysBetween(startDate, endDate)  // 87

// 按类型获取日期范围
const todayRange = getDateRangeByType('today')      // ['2025-03-29 00:00:00', '2025-03-29 23:59:59']
const weekRange = getDateRangeByType('week')        // 本周一到今天
const monthRange = getDateRangeByType('month')      // 本月一号到今天
const yearRange = getDateRangeByType('year')        // 今年一月一号到今天

// 获取本周/本月范围
const [weekStart, weekEnd] = getCurrentWeekRange()   // [周一, 周日]
const [monthStart, monthEnd] = getCurrentMonthRange() // [本月一号, 本月最后一天]

// 日期加减
const tomorrow = dateAdd(now, 'day', 1)         // 明天
const lastWeek = dateAdd(now, 'day', -7)        // 7天前
const nextMonth = dateAdd(now, 'month', 1)      // 下个月
const lastYear = dateAdd(now, 'year', -1)       // 去年

// 添加日期范围到查询参数
const queryParams = { page: 1, limit: 10 }
const dateRange = ['2025-01-01', '2025-03-29']
const paramsWithDate = addDateRange(queryParams, dateRange)
// { page: 1, limit: 10, params: { beginTime: '2025-01-01', endTime: '2025-03-29' } }

// 自定义字段名
const customParams = addDateRange({ page: 1 }, dateRange, 'createTime')
// { page: 1, params: { beginCreateTime: '2025-01-01', endCreateTime: '2025-03-29' } }

格式化占位符说明

占位符说明示例
yyyy / YYYY四位年份2025
MM两位月份01-12
M月份1-12
dd / DD两位日期01-31
d / D日期1-31
HH24小时制小时00-23
H小时0-23
mm两位分钟00-59
m分钟0-59
ss两位秒钟00-59
s秒钟0-59
SSS毫秒000-999
w星期日/一/二/三/四/五/六

数据格式化 format.ts

提供专业的数据展示格式化功能,包括数值格式化、隐私保护、文本处理和状态显示。

函数列表

函数名说明参数返回值
formatUnit格式化 CSS 单位值val: number | string, defaultUnit?: stringstring
formatNumber数字格式化value: number, decimals?: number, useThousandsSeparator?: booleanstring
formatPercent百分比格式化value: number, decimals?: number, withSymbol?: booleanstring
formatAmount金额格式化amount: number, decimals?: number, ...string
formatCurrency货币格式化amount: number, currencyCode?: string, options?: CurrencyFormatOptionsstring
formatFileSize文件大小格式化bytes: number, decimals?: numberstring
formatDistance距离格式化meters: number, decimals?: numberstring
formatDuration时长格式化seconds: number, showZeroHours?: booleanstring
formatPrivacy隐私数据脱敏data: string, options?: PrivacyOptionsstring
formatIDCard身份证脱敏idNumber: string, options?: PrivacyOptionsstring
formatPhone手机号格式化phone: string, format?: string, mask?: string, privacy?: booleanstring
formatBankCard银行卡格式化cardNumber: string, separator?: string, privacy?: booleanstring
formatIPIP 地址格式化ip: string, options?: IPFormatOptionsstring
formatStringLength字符串长度格式化str: string, options?: StringLengthOptionsstring
formatFileName文件名格式化filename: string, maxLength?: numberstring
formatURLURL 格式化url: string, showParams?: booleanstring
formatList列表格式化list: any[], field?: string, options?: ListFormatOptionsstring
formatEnum枚举值格式化value: any, enumMap: object, defaultText?: stringstring
formatBoolean布尔值格式化value: any, trueText?: string, falseText?: stringstring
formatStatusColor状态颜色格式化status: number | string, customColorMap?: objectstring
formatTableCell表格单元格格式化value: any, type: string, options?: objectstring

使用示例

typescript
import {
  formatCurrency,
  formatFileSize,
  formatPrivacy,
  formatPhone,
  formatIDCard,
  formatBankCard,
  formatNumber,
  formatPercent,
  formatDuration,
  formatDistance,
  formatList,
  formatEnum,
  formatBoolean,
  formatStatusColor,
  formatStringLength,
  formatFileName
} from '@/utils/format'

// 货币格式化
const price1 = formatCurrency(1234.56)               // '¥1,234.56'
const price2 = formatCurrency(1234.56, 'USD')        // '$1,234.56'
const price3 = formatCurrency(1234.56, 'EUR')        // '€1,234.56'
const price4 = formatCurrency(1234.56, 'CNY', { decimals: 0 })  // '¥1,235'

// 数字格式化
const num1 = formatNumber(1234567, 0, true)          // '1,234,567'
const num2 = formatNumber(1234.5678, 2)              // '1234.57'
const num3 = formatNumber(1234567.89, 2, true)       // '1,234,567.89'

// 百分比格式化
const percent1 = formatPercent(0.1234)               // '12.34%'
const percent2 = formatPercent(0.1234, 1)            // '12.3%'
const percent3 = formatPercent(0.1234, 2, false)     // '12.34'

// 文件大小格式化
const size1 = formatFileSize(1024)                   // '1.00 KB'
const size2 = formatFileSize(1234567)                // '1.18 MB'
const size3 = formatFileSize(1234567890)             // '1.15 GB'
const size4 = formatFileSize(0)                      // '0 Bytes'

// 距离格式化
const dist1 = formatDistance(500)                    // '500米'
const dist2 = formatDistance(1500)                   // '1.5公里'
const dist3 = formatDistance(12345, 2)               // '12.35公里'

// 时长格式化
const duration1 = formatDuration(3661)               // '1小时1分1秒'
const duration2 = formatDuration(61)                 // '1分1秒'
const duration3 = formatDuration(30)                 // '0分30秒'

// 隐私数据脱敏
const masked1 = formatPrivacy('13812345678', { showStart: 3, showEnd: 4 })
// '138****5678'

const masked2 = formatPrivacy('张三丰', { showStart: 1, maskLength: 2 })
// '张**'

// 手机号格式化
const phone1 = formatPhone('13812345678')                        // '138-1234-5678'
const phone2 = formatPhone('13812345678', 'xxx xxxx xxxx')       // '138 1234 5678'
const phone3 = formatPhone('13812345678', undefined, '*', true)  // '138****5678'

// 身份证脱敏
const idCard = formatIDCard('110101199001011234')    // '1101**********1234'
const idCard2 = formatIDCard('110101199001011234', { showStart: 6, showEnd: 4 })
// '110101********1234'

// 银行卡格式化
const bankCard1 = formatBankCard('6225365271562822')              // '6225 3652 7156 2822'
const bankCard2 = formatBankCard('6225365271562822', '-')         // '6225-3652-7156-2822'
const bankCard3 = formatBankCard('6225365271562822', ' ', true)   // '**** **** **** 2822'

// 字符串长度格式化
const str1 = formatStringLength('这是一个很长的字符串', { maxLength: 10 })
// '这是一个很长...'

const str2 = formatStringLength('这是一个很长的字符串', { maxLength: 10, position: 'middle' })
// '这是一个...字符串'

// 文件名格式化(保留扩展名)
const file1 = formatFileName('very_long_filename_example.txt', 20)
// 'very_long_f...ple.txt'

// 列表格式化
const list1 = formatList([1, 2, 3])                              // '1,2,3'
const list2 = formatList([{id: 1, name: 'A'}, {id: 2, name: 'B'}], 'name')
// 'A,B'
const list3 = formatList([1, 2, 3, 4, 5], null, { maxItems: 3, ellipsis: '等' })
// '1,2,3等'

// 枚举值格式化
const status = formatEnum(1, { 0: '禁用', 1: '启用' })          // '启用'
const level = formatEnum('VIP', { VIP: '会员', NORMAL: '普通' }) // '会员'

// 布尔值格式化
const bool1 = formatBoolean(true)                    // '是'
const bool2 = formatBoolean(false, '启用', '禁用')   // '禁用'
const bool3 = formatBoolean('true')                  // '是'

// 状态颜色格式化
const color1 = formatStatusColor(1)                  // 'success'
const color2 = formatStatusColor(0)                  // 'info'
const color3 = formatStatusColor('error', { error: 'danger' })  // 'danger'

函数增强 function.ts

提供函数式编程工具集,包括性能优化、执行控制、函数转换和异步处理。

函数列表

函数名说明参数返回值
copy复制到剪贴板text: string, message?: stringPromise<void>
withHeaders为请求添加自定义头headers: objectobject
debounce防抖函数fn: Function, delay?: numberFunction
throttle节流函数fn: Function, interval?: numberFunction
once仅执行一次fn: FunctionFunction
delay延迟执行ms: numberPromise<void>
retry重试执行fn: Function, times?: number, delayMs?: numberPromise<any>
withTimeout超时控制promise: Promise, timeout: numberPromise<any>
curry函数柯里化fn: FunctionFunction
partial偏函数应用fn: Function, ...args: any[]Function
memoize记忆化缓存fn: FunctionFunction
serial串行执行tasks: Function[]Promise<any[]>
parallel并行执行tasks: Function[], limit?: numberPromise<any[]>
withRetry带重试的异步执行fn: Function, options?: objectPromise<any>
rateLimit频率限制fn: Function, limit: number, interval: numberFunction
isPresignedUrl判断预签名 URLurl: stringboolean
addCacheBuster添加缓存破坏参数url: stringstring
triggerChartResize触发图表重绘-void

使用示例

typescript
import {
  copy,
  debounce,
  throttle,
  retry,
  delay,
  once,
  memoize,
  withTimeout,
  serial,
  parallel,
  rateLimit
} from '@/utils/function'

// 复制到剪贴板
await copy('要复制的文本', '复制成功!')

// 防抖处理 - 输入停止 300ms 后执行
const debouncedSearch = debounce((keyword: string) => {
  console.log('搜索:', keyword)
  searchAPI(keyword)
}, 300)

// 在输入框事件中使用
inputRef.addEventListener('input', (e) => {
  debouncedSearch(e.target.value)
})

// 节流处理 - 每 100ms 最多执行一次
const throttledScroll = throttle(() => {
  console.log('滚动位置:', window.scrollY)
  updateScrollPosition()
}, 100)

window.addEventListener('scroll', throttledScroll)

// 仅执行一次
const initOnce = once(() => {
  console.log('初始化操作')
  loadResources()
})

initOnce()  // 执行
initOnce()  // 不再执行

// 延迟执行
const showLoadingThenHide = async () => {
  showLoading()
  await delay(2000)  // 延迟 2 秒
  hideLoading()
}

// 重试执行 - 网络请求失败时自动重试
const fetchWithRetry = async (url: string) => {
  return retry(
    () => fetch(url).then(res => res.json()),
    3,      // 最多重试 3 次
    1000    // 每次间隔 1 秒
  )
}

// 超时控制
const fetchWithTimeout = async (url: string) => {
  try {
    const data = await withTimeout(
      fetch(url).then(res => res.json()),
      5000  // 5 秒超时
    )
    return data
  } catch (e) {
    console.error('请求超时或失败:', e)
  }
}

// 记忆化缓存 - 相同参数只计算一次
const expensiveCalculation = memoize((n: number) => {
  console.log('执行计算...')
  let result = 0
  for (let i = 0; i < n; i++) {
    result += Math.sqrt(i)
  }
  return result
})

expensiveCalculation(10000)  // 执行计算
expensiveCalculation(10000)  // 直接返回缓存结果

// 串行执行 - 按顺序执行多个异步任务
const tasks = [
  () => fetchUser(1),
  () => fetchOrders(1),
  () => fetchSettings(1)
]
const results = await serial(tasks)
// 依次执行,返回 [userData, ordersData, settingsData]

// 并行执行 - 限制并发数
const uploadTasks = files.map(file => () => uploadFile(file))
const uploadResults = await parallel(uploadTasks, 3)  // 最多 3 个并发

// 频率限制 - 1 秒内最多执行 5 次
const limitedAPI = rateLimit(
  (id: number) => fetchData(id),
  5,     // 最多 5 次
  1000   // 1 秒内
)

for (let i = 0; i < 10; i++) {
  limitedAPI(i)  // 只有前 5 次会立即执行,其余排队
}

技术实现细节

debounce 实现

typescript
export const debounce = <T extends (...args: any[]) => any>(
  fn: T,
  delay: number = 300
): ((...args: Parameters<T>) => void) => {
  let timer: ReturnType<typeof setTimeout> | null = null

  return function(this: any, ...args: Parameters<T>) {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn.apply(this, args)
      timer = null
    }, delay)
  }
}

throttle 实现

typescript
export const throttle = <T extends (...args: any[]) => any>(
  fn: T,
  interval: number = 100
): ((...args: Parameters<T>) => void) => {
  let lastTime = 0

  return function(this: any, ...args: Parameters<T>) {
    const now = Date.now()
    if (now - lastTime >= interval) {
      fn.apply(this, args)
      lastTime = now
    }
  }
}

数据验证 validators.ts

提供全面的数据验证工具,包括类型验证、格式验证、业务验证和文件验证。

函数列表

函数名说明返回值
isString是否为字符串boolean
isNumber是否为数字boolean
isBoolean是否为布尔值boolean
isArray是否为数组boolean
isObject是否为对象boolean
isFunction是否为函数boolean
isDate是否为日期对象boolean
isNull是否为 nullboolean
isUndefined是否为 undefinedboolean
isEmpty是否为空值boolean
isEmail是否为有效邮箱boolean
isValidURL是否为有效 URLboolean
isIPv4是否为 IPv4 地址boolean
isIPv6是否为 IPv6 地址boolean
isUUID是否为 UUIDboolean
isChinesePhoneNumber是否为中国手机号boolean
isChineseIDCard是否为中国身份证号boolean
isBankCardNumber是否为银行卡号boolean
isPostalCode是否为邮政编码boolean
isImageFile是否为图片文件boolean
isVideoFile是否为视频文件boolean
isAudioFile是否为音频文件boolean
isDocumentFile是否为文档文件boolean
validateFileSize验证文件大小boolean
validateFileType验证文件类型boolean

使用示例

typescript
import {
  isEmail,
  isChinesePhoneNumber,
  isChineseIDCard,
  isValidURL,
  isIPv4,
  isUUID,
  isEmpty,
  isImageFile,
  validateFileSize,
  validateFileType
} from '@/utils/validators'

// 邮箱验证
isEmail('user@example.com')       // true
isEmail('invalid-email')          // false
isEmail('user@domain.cn')         // true

// 手机号验证(中国大陆)
isChinesePhoneNumber('13812345678')   // true
isChinesePhoneNumber('12812345678')   // false(12 开头无效)
isChinesePhoneNumber('1381234567')    // false(位数不对)

// 身份证验证(含校验码验证)
isChineseIDCard('110101199001011234')  // true(需要校验码正确)
isChineseIDCard('11010119900101123X')  // true(X 结尾)
isChineseIDCard('123456789012345678')  // false(校验码错误)

// URL 验证
isValidURL('https://example.com')          // true
isValidURL('http://localhost:8080')        // true
isValidURL('ftp://files.example.com')      // true
isValidURL('example.com')                  // false(缺少协议)

// IP 地址验证
isIPv4('192.168.1.1')      // true
isIPv4('256.1.1.1')        // false(超出范围)
isIPv4('192.168.1')        // false(格式不完整)

// UUID 验证
isUUID('550e8400-e29b-41d4-a716-446655440000')  // true
isUUID('invalid-uuid')                            // false

// 空值验证
isEmpty('')           // true
isEmpty(null)         // true
isEmpty(undefined)    // true
isEmpty([])           // true
isEmpty({})           // true
isEmpty('hello')      // false
isEmpty([1, 2])       // false

// 图片文件验证
isImageFile('photo.jpg')    // true
isImageFile('photo.png')    // true
isImageFile('photo.webp')   // true
isImageFile('doc.pdf')      // false

// 文件大小验证(字节)
validateFileSize(file, 5 * 1024 * 1024)  // 5MB 以内返回 true

// 文件类型验证
validateFileType(file, ['image/jpeg', 'image/png'])  // 验证 MIME 类型

缓存管理 cache.ts

提供浏览器存储封装,支持会话级和持久化的数据存储操作,自动添加应用 ID 前缀避免多应用冲突。

会话缓存 sessionCache

基于 sessionStorage,页面关闭后数据清除。

方法说明参数
set设置缓存key: string, value: string
get获取缓存key: string
getNumber获取数字类型缓存key: string
setJSON设置 JSON 对象key: string, jsonValue: T
getJSON获取 JSON 对象key: string
remove移除缓存key: string
has检查是否存在key: string
clearAll清除所有-

本地缓存 localCache

基于 localStorage,数据永久保存,支持过期时间管理。

方法说明参数
set设置缓存(支持过期时间)key: string, value: T, expireSeconds?: number
get获取缓存key: string
setJSON设置 JSON 对象key: string, jsonValue: T, expireSeconds?: number
getJSON获取 JSON 对象key: string
remove移除缓存key: string
has检查是否存在key: string
clearAll清除所有-
cleanup手动清理过期缓存-
getStats获取存储统计信息-

使用示例

typescript
import { sessionCache, localCache } from '@/utils/cache'

// ========== 会话缓存 ==========

// 存储基本数据
sessionCache.set('userName', 'admin')
sessionCache.set('userId', '12345')

// 获取数据
const userName = sessionCache.get('userName')  // 'admin'
const userId = sessionCache.getNumber('userId')  // 12345

// 存储对象
sessionCache.setJSON('userInfo', {
  id: 1,
  name: '张三',
  role: 'administrator'
})

// 获取对象(支持泛型)
interface UserInfo {
  id: number
  name: string
  role: string
}
const userInfo = sessionCache.getJSON<UserInfo>('userInfo')
console.log(userInfo?.name)  // '张三'

// 检查是否存在
if (sessionCache.has('userToken')) {
  // 令牌存在
}

// 移除缓存
sessionCache.remove('userInfo')

// 清除所有会话缓存
sessionCache.clearAll()

// ========== 本地缓存 ==========

// 存储永久数据
localCache.set('theme', 'dark')
localCache.set('language', 'zh-CN')

// 存储带过期时间的数据(7天过期)
localCache.set('userToken', 'abc123xyz', 7 * 24 * 3600)

// 获取数据(自动处理过期)
const theme = localCache.get<string>('theme')  // 'dark'
const token = localCache.get<string>('userToken')  // 未过期返回值,过期返回 null

// 存储 JSON 对象(30分钟过期)
localCache.setJSON('sysConfig', {
  language: 'zh-CN',
  fontSize: 'medium',
  autoSave: true
}, 30 * 60)

// 获取 JSON 对象
interface SystemConfig {
  language: string
  fontSize: string
  autoSave: boolean
}
const config = localCache.getJSON<SystemConfig>('sysConfig')

// 检查是否存在(包含过期检查)
if (localCache.has('userToken')) {
  // Token 存在且未过期
}

// 手动清理过期缓存
localCache.cleanup()

// 获取存储统计信息
const stats = localCache.getStats()
if (stats) {
  console.log(`总缓存项: ${stats.totalKeys}`)
  console.log(`应用缓存项: ${stats.appKeys}`)
  console.log(`使用率: ${stats.usagePercent}%`)
}

// 清除所有本地缓存
localCache.clearAll()

自动清理机制

本地缓存会自动清理过期数据:

  • 应用启动时执行一次清理
  • 每小时自动清理一次过期缓存
  • 获取数据时自动检测并清理过期项

树形数据 tree.ts

提供专业的树形数据结构处理工具,支持自定义字段名配置。

函数列表

函数名说明返回值
buildTree平铺数组构建树结构T[]
findTreeNode查找树节点T | null
findTreeNodePath查找节点路径T[]
filterTree过滤树节点T[]
flattenTree扁平化树结构T[]
traverseTree遍历树节点void
insertNode插入节点boolean
removeNode删除节点boolean
updateNode更新节点boolean
getLeafNodes获取叶子节点T[]
getTreeDepth计算树深度number

配置选项

typescript
interface TreeOptions {
  id?: string        // ID 字段名,默认 'id'
  parentId?: string  // 父 ID 字段名,默认 'parentId'
  children?: string  // 子节点字段名,默认 'children'
  deepCopy?: boolean // 是否深拷贝,默认 true
}

使用示例

typescript
import {
  buildTree,
  findTreeNode,
  findTreeNodePath,
  flattenTree,
  filterTree,
  traverseTree,
  getLeafNodes,
  getTreeDepth,
  insertNode,
  removeNode,
  updateNode
} from '@/utils/tree'

// 构建树结构
const flatData = [
  { id: 1, name: '公司', parentId: 0 },
  { id: 2, name: '研发部', parentId: 1 },
  { id: 3, name: '销售部', parentId: 1 },
  { id: 4, name: '前端组', parentId: 2 },
  { id: 5, name: '后端组', parentId: 2 }
]

const tree = buildTree(flatData)
// [
//   {
//     id: 1, name: '公司', parentId: 0,
//     children: [
//       { id: 2, name: '研发部', parentId: 1, children: [...] },
//       { id: 3, name: '销售部', parentId: 1, children: [] }
//     ]
//   }
// ]

// 自定义字段名
const customData = [
  { itemId: 1, name: '节点1', pid: 0 },
  { itemId: 2, name: '节点2', pid: 1 }
]
const customTree = buildTree(customData, { id: 'itemId', parentId: 'pid' })

// 查找节点
const node = findTreeNode(tree, node => node.id === 4)
// { id: 4, name: '前端组', parentId: 2 }

const nodeByName = findTreeNode(tree, node => node.name === '销售部')
// { id: 3, name: '销售部', parentId: 1 }

// 查找节点路径
const path = findTreeNodePath(tree, node => node.id === 4)
// [
//   { id: 1, name: '公司', ... },
//   { id: 2, name: '研发部', ... },
//   { id: 4, name: '前端组', ... }
// ]

// 扁平化树结构
const flatArray = flattenTree(tree)
// 返回所有节点的一维数组

// 过滤树节点
const filtered = filterTree(tree, node => node.name.includes('研发'))
// 保留包含"研发"的节点及其父节点

// 遍历树节点
traverseTree(tree, (node, parent, level) => {
  console.log(`节点: ${node.name}, 父节点: ${parent?.name}, 层级: ${level}`)
  node.level = level  // 添加层级属性
})

// 获取叶子节点
const leaves = getLeafNodes(tree)
// [{ id: 4, ... }, { id: 5, ... }, { id: 3, ... }]

// 计算树深度
const depth = getTreeDepth(tree)  // 3

// 插入节点
const newNode = { id: 6, name: '测试组', parentId: 2 }
insertNode(tree, newNode, 2)  // 插入到 id=2 的节点下

// 删除节点
removeNode(tree, node => node.id === 5)  // 删除 id=5 的节点

// 更新节点
updateNode(
  tree,
  node => node.id === 4,
  node => ({ ...node, name: '大前端组', status: 'active' })
)

异步错误处理 to.ts

将 Promise 和可能抛出异常的代码转换为 [error, data] 格式,避免 try-catch 的使用,让代码更加简洁和可读。

函数列表

函数名说明返回值
to基础 Promise 异常处理Promise<[Error | null, T | null]>
toValidate表单验证专用Promise<[Error | null, boolean]>
toAll批量处理 PromisePromise<Array<[Error | null, T | null]>>
toWithTimeout带超时控制Promise<[Error | null, T | null]>
toSync同步版本[Error | null, T | null]
toResult类型安全透传Promise<[Error | null, T | null]>
toWithRetry自动重试机制Promise<[Error | null, T | null]>
toWithDefault带默认值Promise<[Error | null, T]>
toWithLog带调试日志Promise<[Error | null, T | null]>
toSequence串行执行Promise<[Error | null, T[]]>
toIf条件执行Promise<[Error | null, T | null]>

使用示例

typescript
import {
  to,
  toValidate,
  toAll,
  toWithTimeout,
  toWithRetry,
  toWithDefault,
  toSequence,
  toSync
} from '@/utils/to'

// 基础用法 - 替代 try-catch
const [err, user] = await to(fetchUser(userId))
if (err) {
  console.error('获取用户失败:', err.message)
  return
}
console.log('用户信息:', user)

// 表单验证
const handleSubmit = async () => {
  const [validErr, isValid] = await toValidate(formRef.value)
  if (validErr || !isValid) {
    ElMessage.error(validErr?.message || '表单验证失败')
    return
  }

  const [submitErr] = await to(submitForm(formData))
  if (submitErr) {
    ElMessage.error('提交失败: ' + submitErr.message)
    return
  }

  ElMessage.success('提交成功')
}

// 批量请求 - 并行执行多个接口
const results = await toAll([
  fetchUserInfo(userId),
  fetchUserOrders(userId),
  fetchUserPreferences(userId)
])

const [userErr, userInfo] = results[0]
const [ordersErr, orders] = results[1]
const [prefsErr, preferences] = results[2]

// 带超时控制
const [err, data] = await toWithTimeout(
  fetch('/api/heavy-computation'),
  5000,  // 5秒超时
  '计算超时,请稍后重试'
)

// 带重试机制
const [err, data] = await toWithRetry(
  () => fetch('/api/unstable-endpoint'),
  3,     // 重试 3 次
  2000   // 间隔 2 秒
)

// 带默认值
const [err, config] = await toWithDefault(
  getUserConfig(userId),
  { theme: 'light', language: 'zh-CN', pageSize: 10 }
)
// config 保证不为 null

// 串行执行
const initSteps = [
  () => connectToDatabase(),
  () => loadConfiguration(),
  () => startServices()
]
const [err, results] = await toSequence(initSteps)
if (err) {
  console.error('初始化失败:', err.message)
  return
}

// 同步版本 - 处理 JSON 解析
const [parseErr, data] = toSync(() => JSON.parse(jsonString))
if (parseErr) {
  console.error('JSON 解析失败:', parseErr.message)
  return
}

UI 交互 modal.ts

封装 Element Plus 的弹窗消息功能,提供统一的 UI 交互接口。

函数列表

函数名说明返回值
showMsgSuccess成功消息void
showMsgError错误消息void
showMsgWarning警告消息void
showMsgInfo信息消息void
showConfirm确认对话框Promise<[Error | null, boolean]>
showConfirmDanger危险确认对话框Promise<[Error | null, boolean]>
showPrompt输入对话框Promise<[Error | null, string]>
showAlert警告对话框Promise<void>
showNotifySuccess成功通知void
showNotifyError错误通知void
showNotifyWarning警告通知void
showNotifyInfo信息通知void
showLoading显示加载遮罩void
hideLoading隐藏加载遮罩void

使用示例

typescript
import {
  showMsgSuccess,
  showMsgError,
  showConfirm,
  showConfirmDanger,
  showLoading,
  hideLoading,
  showNotifySuccess,
  showPrompt
} from '@/utils/modal'

// 消息提示
showMsgSuccess('操作成功!')
showMsgError('操作失败,请重试')
showMsgWarning('请注意数据安全')
showMsgInfo('这是一条提示信息')

// 确认对话框
const [err] = await showConfirm('确定要提交这条记录吗?')
if (!err) {
  // 用户点击了确认
  handleSubmit()
}

// 危险确认(红色按钮)
const [err] = await showConfirmDanger('确定要删除这条记录吗?此操作不可恢复!')
if (!err) {
  handleDelete()
}

// 输入对话框
const [err, inputValue] = await showPrompt('请输入备注信息', '备注')
if (!err && inputValue) {
  console.log('用户输入:', inputValue)
}

// 通知消息
showNotifySuccess('数据保存成功')
showNotifyError('服务器连接失败')

// 加载遮罩
showLoading('数据加载中...')
try {
  await fetchData()
} finally {
  hideLoading()
}

页面管理 tab.ts

提供标签页导航控制功能,管理多标签页的打开、关闭和刷新操作。

函数列表

函数名说明
refreshPage刷新当前页面
closePage关闭当前页面
closeOtherPage关闭其他页面
closeAllPage关闭所有页面
closeLeftPage关闭左侧页面
closeRightPage关闭右侧页面
openPage打开新页面
updatePageTitle更新页面标题

使用示例

typescript
import {
  refreshPage,
  closePage,
  openPage,
  closeOtherPage,
  updatePageTitle
} from '@/utils/tab'

// 刷新当前页面
refreshPage()

// 打开新页面
openPage('/user/detail', '用户详情', { id: '123' })

// 关闭当前页面并返回列表
closePage('/user/list')

// 关闭其他页面
closeOtherPage()

// 更新页面标题
updatePageTitle('编辑用户 - 张三')

布尔值处理 boolean.ts

提供布尔值的判断、转换和切换操作。

函数列表

函数名说明返回值
isTrue多格式真值判断boolean
isFalse多格式假值判断boolean
toBool转换为布尔值boolean
toBoolString转换为布尔字符串'1' | '0'
toggleStatus切换状态'1' | '0'

使用示例

typescript
import { isTrue, isFalse, toBoolString, toggleStatus, toBool } from '@/utils/boolean'

// 多格式布尔判断
isTrue('1')        // true
isTrue('true')     // true
isTrue('yes')      // true
isTrue(1)          // true
isTrue(true)       // true

isFalse('0')       // true
isFalse('false')   // true
isFalse('no')      // true
isFalse(null)      // true
isFalse(undefined) // true

// 转换为布尔值
toBool('true')     // true
toBool('1')        // true
toBool('false')    // false
toBool('0')        // false

// 转换为布尔字符串(常用于后端交互)
toBoolString('yes')     // '1'
toBoolString(true)      // '1'
toBoolString('no')      // '0'
toBoolString(false)     // '0'

// 状态切换
toggleStatus('1')  // '0'
toggleStatus('0')  // '1'
toggleStatus('true')  // '0'

使用方式

按需导入(推荐)

typescript
// 从具体文件导入需要的函数
import { formatDate, getCurrentDateTime } from '@/utils/date'
import { isEmail, isChinesePhoneNumber } from '@/utils/validators'
import { deepClone, get, pick } from '@/utils/object'
import { to, toValidate } from '@/utils/to'

// 使用函数
const dateStr = formatDate(new Date(), 'yyyy-MM-dd')
const isValid = isEmail('user@example.com')
const copy = deepClone(originalData)
const [err, data] = await to(fetchData())

统一入口导入

typescript
// 从统一入口导入(需要配置 index.ts)
import {
  formatDate,
  isEmail,
  deepClone,
  to
} from '@/utils'

全量导入(不推荐)

typescript
// 不推荐:会增加打包体积
import * as utils from '@/utils'
import * as dateUtils from '@/utils/date'

工具函数统计

分类文件名函数数量主要功能
字符串处理string.ts20+字符串操作、格式转换、URL 处理、验证
对象操作object.ts17+对象处理、数组操作、数据转换、属性访问
日期时间date.ts18+日期格式化、时间计算、范围获取
数据格式化format.ts21+数值、文本、隐私数据、状态格式化
函数增强function.ts18+防抖节流、异步控制、函数式编程
数据验证validators.ts25+类型验证、格式验证、业务验证
布尔处理boolean.ts5+布尔值判断、转换、切换
加密安全crypto.ts10+AES 加密、哈希计算
RSA 加密rsa.ts5+RSA 加解密、数字签名
缓存管理cache.ts20+浏览器存储封装、过期管理
DOM 操作class.ts7+CSS 类名操作
滚动控制scroll.ts6+页面滚动、元素可见性
树形数据tree.ts11+树结构构建、操作、遍历
UI 交互modal.ts14+消息提示、弹窗对话
页面管理tab.ts8+标签页操作、路由管理
错误处理to.ts11+异步错误处理、Promise 包装

最佳实践

1. 按需导入,减少打包体积

typescript
// ✅ 推荐:只导入需要的函数
import { formatDate } from '@/utils/date'
import { isEmail } from '@/utils/validators'

// ❌ 不推荐:导入整个模块
import * as dateUtils from '@/utils/date'

2. 使用 TypeScript 类型提示

typescript
// 利用完整的类型提示
import { TreeNode, buildTree } from '@/utils/tree'

interface Department extends TreeNode {
  id: number
  name: string
  parentId: number
}

const departments: Department[] = [/* ... */]
const tree = buildTree<Department>(departments)

3. 组合使用提升效率

typescript
import { to } from '@/utils/to'
import { formatCurrency } from '@/utils/format'
import { showMsgError, showMsgSuccess } from '@/utils/modal'

const handleSubmit = async () => {
  const [err, result] = await to(submitOrder())

  if (err) {
    showMsgError('提交失败:' + err.message)
    return
  }

  const amount = formatCurrency(result.totalAmount)
  showMsgSuccess(`订单提交成功,金额:${amount}`)
}

4. 统一错误处理模式

typescript
import { to, toValidate } from '@/utils/to'

const handleFormSubmit = async () => {
  // 1. 表单验证
  const [validErr, isValid] = await toValidate(formRef.value)
  if (validErr || !isValid) {
    return
  }

  // 2. 数据提交
  const [submitErr, result] = await to(submitData())
  if (submitErr) {
    handleError(submitErr)
    return
  }

  // 3. 成功处理
  handleSuccess(result)
}

5. 缓存数据正确使用

typescript
import { localCache, sessionCache } from '@/utils/cache'

// 敏感数据使用会话缓存
sessionCache.setJSON('userSession', { token: 'xxx', expireAt: Date.now() + 3600000 })

// 用户偏好使用本地缓存(带过期时间)
localCache.setJSON('userPreferences', { theme: 'dark' }, 30 * 24 * 3600)

// 获取时进行空值检查
const prefs = localCache.getJSON('userPreferences') ?? defaultPreferences

6. 树形数据高效处理

typescript
import { buildTree, findTreeNode, flattenTree } from '@/utils/tree'

// 构建树时指定字段映射
const menuTree = buildTree(menuList, {
  id: 'menuId',
  parentId: 'parentMenuId',
  children: 'subMenus'
})

// 查找节点时使用精确条件
const targetNode = findTreeNode(menuTree, node => node.menuId === targetId)

// 批量操作时先扁平化
const allNodes = flattenTree(menuTree)
const activeNodes = allNodes.filter(node => node.status === 'active')

通过这些工具函数,可以大大提升开发效率,减少重复代码,让项目代码更加优雅和可维护。每个工具都经过精心设计,支持 TypeScript 类型提示,并提供了完整的使用示例和最佳实践。