知识库首页 模版 utils.ts.md

utils.ts

本地来源:模版/template/raphael-starterkit-v1-main/analysis/06-工具函数/utils.ts.md

工具函数详细分析

文件概述

lib/utils.tsutils/utils.ts 包含项目中使用的核心工具函数,提供类名合并、数据格式化、验证等功能。

lib/utils.ts 分析

import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

导入分析: - ClassValue: clsx的类型定义,支持多种类名格式 - clsx: 条件类名处理库,用于动态类名组合 - twMerge: Tailwind CSS类名合并工具,智能处理冲突

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

类名合并函数: - cn: classNames的缩写,项目中最常用的工具函数 - ...inputs: ClassValue[]: 支持多种输入格式的类名参数 - clsx(inputs): 先使用clsx处理条件逻辑和格式化 - twMerge(): 再使用twMerge智能合并Tailwind类名

使用示例

// 基础用法
cn('px-2 py-1', 'text-sm')
// 输出: "px-2 py-1 text-sm"

// 条件类名
cn('base-class', {
  'active-class': isActive,
  'disabled-class': isDisabled
})

// 冲突类名合并(twMerge的优势)
cn('px-2 px-4') // 输出: "px-4" (后者覆盖前者)
cn('text-red-500 text-blue-500') // 输出: "text-blue-500"

utils/utils.ts 分析

export function formatDate(date: Date | string): string {
  const d = typeof date === 'string' ? new Date(date) : date;
  return d.toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit'
  });
}

日期格式化函数: - 支持Date对象和字符串输入 - 使用中文本地化格式 - 返回YYYY-MM-DD格式

export function formatCurrency(amount: number, currency: string = 'CNY'): string {
  return new Intl.NumberFormat('zh-CN', {
    style: 'currency',
    currency: currency,
    minimumFractionDigits: 2,
    maximumFractionDigits: 2
  }).format(amount);
}

货币格式化函数: - 使用Intl.NumberFormat进行本地化 - 默认使用人民币(CNY) - 保留两位小数

export function debounce<T extends (...args: any[]) => any>(
  func: T,
  wait: number
): (...args: Parameters<T>) => void {
  let timeout: NodeJS.Timeout;

  return (...args: Parameters<T>) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => func(...args), wait);
  };
}

防抖函数: - 泛型实现,保持类型安全 - 使用setTimeout实现防抖逻辑 - 返回延迟执行的函数

export function throttle<T extends (...args: any[]) => any>(
  func: T,
  limit: number
): (...args: Parameters<T>) => void {
  let inThrottle: boolean;

  return (...args: Parameters<T>) => {
    if (!inThrottle) {
      func(...args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

节流函数: - 限制函数执行频率 - 使用标志位控制执行 - 适用于高频事件处理

export function validateEmail(email: string): boolean {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
}

邮箱验证函数: - 使用正则表达式验证 - 简单但实用的邮箱格式检查 - 返回布尔值

export function generateId(length: number = 8): string {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  let result = '';

  for (let i = 0; i < length; i++) {
    result += chars.charAt(Math.floor(Math.random() * chars.length));
  }

  return result;
}

ID生成函数: - 生成指定长度的随机ID - 使用字母和数字组合 - 默认生成8位ID

export function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

延迟函数: - 返回Promise,支持async/await - 用于测试或模拟异步操作 - 简洁的setTimeout封装

export function capitalize(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}

首字母大写函数: - 将字符串首字母转为大写 - 其余字母转为小写 - 常用于用户名格式化

export function truncateText(text: string, maxLength: number): string {
  if (text.length <= maxLength) return text;
  return text.slice(0, maxLength - 3) + '...';
}

文本截断函数: - 超出长度时添加省略号 - 保留指定长度的文本 - 用于UI中的文本截断显示

设计模式分析

1. 纯函数模式

  • 所有函数都是纯函数,无副作用
  • 相同输入始终产生相同输出
  • 易于测试和调试

2. 泛型设计模式

  • 使用TypeScript泛型保持类型安全
  • 提供灵活的函数签名
  • 编译时类型检查

3. 组合模式

  • cn函数组合了clsxtwMerge
  • 发挥各自优势,提供最佳体验
  • 抽象复杂的组合逻辑

4. 工厂模式

  • debouncethrottle返回新函数
  • 根据参数创建定制化函数
  • 闭包保持状态

性能优化

1. 类名合并优化

// 避免重复计算
const buttonClasses = cn(
  'base-button-classes',
  variant === 'primary' && 'primary-classes',
  size === 'large' && 'large-classes'
);

2. 防抖节流应用

// 搜索输入防抖
const debouncedSearch = debounce((query: string) => {
  performSearch(query);
}, 300);

// 滚动事件节流
const throttledScroll = throttle(() => {
  updateScrollPosition();
}, 100);

使用示例

1. 类名合并

// 组件中使用
const Button = ({ variant, size, disabled, className, ...props }) => {
  return (
    <button
      className={cn(
        'px-4 py-2 rounded font-medium',
        {
          'bg-blue-500 text-white': variant === 'primary',
          'bg-gray-200 text-gray-800': variant === 'secondary',
          'opacity-50 cursor-not-allowed': disabled,
        },
        size === 'sm' && 'text-sm px-2 py-1',
        size === 'lg' && 'text-lg px-6 py-3',
        className
      )}
      {...props}
    />
  );
};

2. 表单验证

// 表单验证示例
const validateForm = (formData: FormData) => {
  const email = formData.get('email') as string;
  const errors: string[] = [];

  if (!validateEmail(email)) {
    errors.push('请输入有效的邮箱地址');
  }

  return errors;
};

3. 数据格式化

// 显示格式化数据
const OrderSummary = ({ order }) => {
  return (
    <div>
      <p>订单日期: {formatDate(order.createdAt)}</p>
      <p>订单金额: {formatCurrency(order.amount)}</p>
      <p>订单描述: {truncateText(order.description, 100)}</p>
    </div>
  );
};

扩展建议

1. 更多验证函数

export function validatePassword(password: string): {
  isValid: boolean;
  errors: string[];
} {
  const errors: string[] = [];

  if (password.length < 8) {
    errors.push('密码至少需要8个字符');
  }

  if (!/[A-Z]/.test(password)) {
    errors.push('密码需要包含大写字母');
  }

  if (!/[a-z]/.test(password)) {
    errors.push('密码需要包含小写字母');
  }

  if (!/[0-9]/.test(password)) {
    errors.push('密码需要包含数字');
  }

  return {
    isValid: errors.length === 0,
    errors
  };
}

2. 更多格式化函数

export function formatFileSize(bytes: number): string {
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
  if (bytes === 0) return '0 Bytes';

  const i = Math.floor(Math.log(bytes) / Math.log(1024));
  return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
}

export function formatRelativeTime(date: Date): string {
  const now = new Date();
  const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);

  if (diffInSeconds < 60) return '刚刚';
  if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}分钟前`;
  if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}小时前`;
  return `${Math.floor(diffInSeconds / 86400)}天前`;
}

最佳实践

  1. 类型安全: 使用TypeScript类型定义
  2. 纯函数: 避免副作用,保持函数纯净
  3. 性能优化: 合理使用防抖和节流
  4. 错误处理: 提供完整的错误处理逻辑
  5. 文档注释: 为复杂函数提供JSDoc注释
  6. 单元测试: 为工具函数编写测试用例

这些工具函数构成了项目的基础设施,提供了类名处理、数据格式化、验证和性能优化等核心功能,为整个应用的开发提供了强有力的支持。

本文档为站内渲染。原始文件本地路径:saas/source/templates/模版-template-raphael-starterkit-v1-main-analysis-06-工具函数-util-c731a2.md(仅本地保留,不入库不部署)