Skip to content

封装高复用性工具方法 ​

一、核心概念与快速记忆 ​

text
需求分析 → 方法设计 → 代码实现 → 单元测试 → Rollup构建 → 发布npm → 团队复用

核心串联: 需求分析 ⇒ 设计实现 ⇒ 测试构建 ⇒ 发布复用

二、大厂前端团队常用工具方法分类 ​

1. 数据处理类 ​

数组处理 ​

  • 去重:高性能数组去重,支持复杂数据类型
  • 排序:多字段排序,支持自定义排序规则
  • 分组:根据指定字段分组,支持多级分组
  • 扁平化:嵌套数组扁平化,支持指定深度

对象处理 ​

  • 深拷贝:高性能深拷贝,支持循环引用
  • 合并:智能合并对象,支持自定义合并策略
  • 属性筛选:根据规则筛选对象属性
  • 属性映射:对象属性名映射转换

字符串处理 ​

  • 格式化:模板字符串格式化,支持对象插值
  • 脱敏:手机号、身份证、银行卡号脱敏
  • 转换:驼峰/下划线/连字符转换
  • 验证:邮箱、手机号、URL等格式验证

2. 日期时间类 ​

  • 格式化:自定义日期时间格式,支持多语言
  • 计算时间差:精确计算两个日期的时间差
  • 相对时间:转换为"3分钟前"、"2天前"等相对时间
  • 时区处理:跨时区日期时间转换

3. 性能优化类 ​

  • 防抖节流:优化高频事件处理
  • 懒加载:图片和组件懒加载
  • 缓存管理:内存缓存和本地缓存封装
  • 虚拟列表:大数据量列表优化

4. 工具函数类 ​

  • 类型判断:精确的类型检测,支持所有JavaScript类型
  • URL处理:URL参数解析和生成
  • 随机数生成:安全的随机数和随机字符串生成
  • DOM操作:简化DOM元素的选择和操作

5. 安全加密类 ​

  • Base64编解码:安全的Base64编码和解码
  • MD5/SHA加密:常用哈希算法封装
  • 数据脱敏:敏感数据的安全处理
  • XSS防护:防止跨站脚本攻击

6. 网络请求类 ​

  • axios二次封装:统一的请求配置和拦截器
  • 缓存策略:请求结果缓存,减少网络请求
  • 重试机制:失败自动重试,提高请求成功率
  • 取消请求:支持取消正在进行的请求

三、核心逻辑与实现示例 ​

1. 防抖函数(Debounce) ​

功能说明:限制函数在一定时间内只能执行一次,常用于搜索框输入、窗口 resize 等场景。

核心逻辑:

  1. 每次调用函数时,清除之前的定时器
  2. 设置新的定时器,延迟执行目标函数
  3. 支持立即执行选项

代码示例:

javascript
/**
 * 防抖函数
 * @param {Function} fn - 要执行的函数
 * @param {number} delay - 延迟时间,单位毫秒
 * @param {boolean} immediate - 是否立即执行
 * @returns {Function} 防抖处理后的函数
 */
export const debounce = (fn, delay = 300, immediate = false) => {
  let timer = null;
  
  return function(...args) {
    const context = this;
    
    // 清除之前的定时器
    if (timer) clearTimeout(timer);
    
    // 立即执行
    if (immediate && !timer) {
      fn.apply(context, args);
    }
    
    // 设置新的定时器
    timer = setTimeout(() => {
      fn.apply(context, args);
      timer = null;
    }, delay);
  };
};

使用场景:

  • 搜索框实时搜索
  • 窗口大小调整
  • 滚动事件处理

2. 深拷贝函数(Deep Clone) ​

功能说明:创建一个对象的深拷贝,支持循环引用和所有JavaScript数据类型。

核心逻辑:

  1. 处理基本类型和null
  2. 处理日期和正则对象
  3. 处理数组和对象
  4. 使用WeakMap解决循环引用
  5. 递归拷贝嵌套结构

代码示例:

javascript
/**
 * 深拷贝函数
 * @param {any} source - 要拷贝的源对象
 * @param {WeakMap} cache - 用于处理循环引用的缓存
 * @returns {any} 拷贝后的对象
 */
export const deepClone = (source, cache = new WeakMap()) => {
  // 处理基本类型和null
  if (source === null || typeof source !== 'object') {
    return source;
  }
  
  // 处理日期对象
  if (source instanceof Date) {
    return new Date(source.getTime());
  }
  
  // 处理正则对象
  if (source instanceof RegExp) {
    return new RegExp(source);
  }
  
  // 处理循环引用
  if (cache.has(source)) {
    return cache.get(source);
  }
  
  // 处理数组和对象
  const target = Array.isArray(source) ? [] : {};
  cache.set(source, target);
  
  // 拷贝所有可枚举属性
  for (const key in source) {
    if (Object.prototype.hasOwnProperty.call(source, key)) {
      target[key] = deepClone(source[key], cache);
    }
  }
  
  // 拷贝Symbol属性
  const symbolKeys = Object.getOwnPropertySymbols(source);
  for (const symbolKey of symbolKeys) {
    target[symbolKey] = deepClone(source[symbolKey], cache);
  }
  
  return target;
};

使用场景:

  • 状态管理中的对象拷贝
  • 表单数据处理
  • 复杂数据结构的复制

3. 相对时间转换 ​

功能说明:将日期转换为相对时间,如"3分钟前"、"2天前"等。

核心逻辑:

  1. 计算目标日期与当前日期的时间差
  2. 根据时间差的大小,返回不同的相对时间格式
  3. 支持多语言和自定义格式

代码示例:

javascript
/**
 * 相对时间转换
 * @param {Date|number|string} date - 目标日期
 * @returns {string} 相对时间字符串
 */
export const formatRelativeTime = (date) => {
  const targetDate = new Date(date);
  const now = new Date();
  const diff = now - targetDate;
  
  // 转换为秒、分钟、小时、天、月、年
  const second = Math.floor(diff / 1000);
  const minute = Math.floor(second / 60);
  const hour = Math.floor(minute / 60);
  const day = Math.floor(hour / 24);
  const month = Math.floor(day / 30);
  const year = Math.floor(month / 12);
  
  if (year > 0) {
    return `${year}年前`;
  } else if (month > 0) {
    return `${month}个月前`;
  } else if (day > 0) {
    return `${day}天前`;
  } else if (hour > 0) {
    return `${hour}小时前`;
  } else if (minute > 0) {
    return `${minute}分钟前`;
  } else if (second > 0) {
    return `${second}秒前`;
  } else {
    return '刚刚';
  }
};

使用场景:

  • 社交应用中的动态时间
  • 评论和消息的时间显示
  • 日志记录的时间格式化

4. URL参数处理 ​

功能说明:解析URL参数为对象,或根据对象生成URL参数字符串。

核心逻辑:

  1. 解析URL中的查询字符串
  2. 支持数组和对象类型的参数
  3. 生成符合规范的查询字符串

代码示例:

javascript
/**
 * URL参数处理工具
 */
export const urlParams = {
  /**
   * 解析URL参数为对象
   * @param {string} url - 可选,URL字符串,默认使用当前页面URL
   * @returns {object} 解析后的参数对象
   */
  parse(url) {
    const queryString = url ? new URL(url).search.slice(1) : window.location.search.slice(1);
    const params = {};
    
    if (!queryString) return params;
    
    queryString.split('&').forEach(pair => {
      const [key, value] = pair.split('=');
      const decodedKey = decodeURIComponent(key);
      const decodedValue = decodeURIComponent(value || '');
      
      // 处理数组参数
      if (decodedKey.endsWith('[]')) {
        const arrKey = decodedKey.slice(0, -2);
        if (!params[arrKey]) {
          params[arrKey] = [];
        }
        params[arrKey].push(decodedValue);
      } else {
        params[decodedKey] = decodedValue;
      }
    });
    
    return params;
  },
  
  /**
   * 根据对象生成URL参数字符串
   * @param {object} params - 参数对象
   * @returns {string} URL参数字符串
   */
  stringify(params) {
    const parts = [];
    
    Object.entries(params).forEach(([key, value]) => {
      if (value === null || value === undefined) return;
      
      if (Array.isArray(value)) {
        // 处理数组参数
        value.forEach(item => {
          parts.push(`${encodeURIComponent(key)}[]=${encodeURIComponent(item)}`);
        });
      } else {
        parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
      }
    });
    
    return parts.length ? `?${parts.join('&')}` : '';
  }
};

使用场景:

  • 页面间参数传递
  • API请求参数处理
  • 路由参数管理

5. 类型判断工具 ​

功能说明:精确判断JavaScript数据类型,比typeof更准确。

核心逻辑:

  1. 利用Object.prototype.toString.call()获取准确类型
  2. 提取类型名称并转换为小写
  3. 支持所有JavaScript内置类型

代码示例:

javascript
/**
 * 类型判断工具
 */
export const type = {
  /**
   * 获取值的准确类型
   * @param {any} value - 要判断的值
   * @returns {string} 类型名称
   */
  get(value) {
    if (value === null) return 'null';
    if (typeof value === 'undefined') return 'undefined';
    return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
  },
  
  /**
   * 判断是否为数组
   * @param {any} value - 要判断的值
   * @returns {boolean} 是否为数组
   */
  isArray(value) {
    return this.get(value) === 'array';
  },
  
  /**
   * 判断是否为对象
   * @param {any} value - 要判断的值
   * @returns {boolean} 是否为对象
   */
  isObject(value) {
    return this.get(value) === 'object';
  },
  
  /**
   * 判断是否为函数
   * @param {any} value - 要判断的值
   * @returns {boolean} 是否为函数
   */
  isFunction(value) {
    return this.get(value) === 'function';
  },
  
  /**
   * 判断是否为字符串
   * @param {any} value - 要判断的值
   * @returns {boolean} 是否为字符串
   */
  isString(value) {
    return this.get(value) === 'string';
  },
  
  /**
   * 判断是否为数字
   * @param {any} value - 要判断的值
   * @returns {boolean} 是否为数字
   */
  isNumber(value) {
    return this.get(value) === 'number' && !isNaN(value);
  },
  
  /**
   * 判断是否为布尔值
   * @param {any} value - 要判断的值
   * @returns {boolean} 是否为布尔值
   */
  isBoolean(value) {
    return this.get(value) === 'boolean';
  },
  
  /**
   * 判断是否为日期对象
   * @param {any} value - 要判断的值
   * @returns {boolean} 是否为日期对象
   */
  isDate(value) {
    return this.get(value) === 'date' && !isNaN(value.getTime());
  },
  
  /**
   * 判断是否为正则表达式
   * @param {any} value - 要判断的值
   * @returns {boolean} 是否为正则表达式
   */
  isRegExp(value) {
    return this.get(value) === 'regexp';
  },
  
  /**
   * 判断是否为Symbol
   * @param {any} value - 要判断的值
   * @returns {boolean} 是否为Symbol
   */
  isSymbol(value) {
    return this.get(value) === 'symbol';
  }
};

使用场景:

  • 数据校验和处理
  • 函数参数类型检查
  • 通用工具函数内部使用

四、Rollup构建发布流程 ​

1. 项目初始化 ​

bash
# 创建项目目录
mkdir my-utils
cd my-utils

# 初始化package.json
npm init -y

# 安装Rollup和相关插件
npm install -D rollup @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-typescript tslib typescript

2. Rollup配置文件 ​

javascript
// rollup.config.js
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import typescript from '@rollup/plugin-typescript';

export default {
  input: 'src/index.ts',
  output: [
    // ESM格式,用于现代浏览器和ES模块系统
    {
      file: 'dist/my-utils.esm.js',
      format: 'esm',
      sourcemap: true
    },
    // CJS格式,用于Node.js环境
    {
      file: 'dist/my-utils.cjs.js',
      format: 'cjs',
      sourcemap: true
    },
    // UMD格式,用于浏览器全局变量和AMD模块系统
    {
      file: 'dist/my-utils.umd.js',
      format: 'umd',
      name: 'MyUtils',
      sourcemap: true
    }
  ],
  plugins: [
    resolve(),
    commonjs(),
    typescript()
  ]
};

3. TypeScript配置 ​

json
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2018",
    "module": "ESNext",
    "lib": ["ES2018", "DOM"],
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

4. 项目结构 ​

my-utils/
├── src/
│   ├── index.ts          # 主入口文件
│   ├── array.ts          # 数组工具方法
│   ├── object.ts         # 对象工具方法
│   ├── string.ts         # 字符串工具方法
│   ├── date.ts           # 日期工具方法
│   └── utils.ts          # 通用工具方法
├── dist/                 # 构建输出目录
├── rollup.config.js      # Rollup配置
├── tsconfig.json         # TypeScript配置
└── package.json          # 项目配置

5. 构建命令 ​

在package.json中添加构建脚本:

json
{
  "scripts": {
    "build": "rollup -c",
    "build:watch": "rollup -c -w"
  }
}

执行构建:

bash
npm run build

6. 发布到npm ​

bash
# 登录npm
npm login

# 发布包
npm publish

五、完整流程箭头指向说明 ​

┌─────────────────┐     ┌───────────────┐     ┌───────────────┐
│  需求分析与设计  │────▶│  代码实现     │────▶│  单元测试     │
└─────────────────┘     └───────────────┘     └───────────────┘
                              ▲                       │
                              │                       ▼
                        ┌───────────────┐     ┌───────────────┐
                        │  文档编写     │◀────│  Rollup构建   │
                        └───────────────┘     └───────────────┘
                              ▲                       │
                              │                       ▼
                        ┌───────────────┐     ┌───────────────┐
                        │  团队评审     │◀────│  npm发布      │
                        └───────────────┘     └───────────────┘
                              ▲                       │
                              │                       ▼
                        ┌───────────────┐     ┌───────────────┐
                        │  持续维护     │◀────│  团队复用     │
                        └───────────────┘     └───────────────┘

六、最佳实践与注意事项 ​

1. 设计原则 ​

  • 单一职责:每个工具方法只做一件事
  • 可测试性:便于编写单元测试
  • 性能优化:考虑边界情况和性能瓶颈
  • 类型安全:使用TypeScript保证类型安全
  • 向后兼容:避免破坏性变更

2. 命名规范 ​

  • 方法名使用小驼峰命名法
  • 语义化命名,清晰表达功能
  • 避免过于缩写,保证可读性
  • 使用前缀区分不同类型的方法

3. 文档编写 ​

  • 为每个方法编写详细的JSDoc注释
  • 说明参数类型、返回值和使用场景
  • 提供使用示例
  • 维护更新日志

4. 测试策略 ​

  • 编写单元测试,覆盖率≥90%
  • 测试边界情况和异常输入
  • 集成测试确保整体功能正常
  • 使用CI/CD自动化测试

5. 版本管理 ​

  • 遵循语义化版本控制
  • 及时更新CHANGELOG.md
  • 发布前进行充分测试
  • 考虑使用changesets管理版本

七、大厂实践案例 ​

1. 阿里巴巴 - 通用工具库 ​

  • 特点:模块化设计,按需引入
  • 技术栈:TypeScript + Rollup
  • 核心功能:数据处理、日期时间、性能优化
  • 使用范围:集团内部所有前端项目

2. 腾讯 - 业务工具库 ​

  • 特点:业务导向,深度集成业务场景
  • 技术栈:JavaScript + Webpack
  • 核心功能:微信生态工具、支付相关工具
  • 使用范围:腾讯内部业务线

3. 字节跳动 - 性能工具库 ​

  • 特点:性能优先,极致优化
  • 技术栈:TypeScript + Rollup
  • 核心功能:防抖节流、虚拟列表、缓存管理
  • 使用范围:字节跳动各产品线

八、总结 ​

封装高复用性工具方法是前端工程化的重要组成部分,能够显著提高开发效率和代码质量。通过遵循一定的设计原则和流程,结合Rollup等构建工具,可以创建高质量的工具库,供团队内部复用。

大厂前端团队通常会根据业务需求和技术栈,封装一系列常用的工具方法,涵盖数据处理、日期时间、性能优化、安全加密等多个方面。这些工具方法经过充分测试和实践验证,能够有效解决实际开发中的各种问题。

通过学习和借鉴大厂的实践经验,我们可以不断优化自己的工具库,提高团队的开发效率和代码质量,为项目的成功交付提供有力支持。

Released under the MIT License.