封装高复用性工具方法
一、核心概念与快速记忆
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 等场景。
核心逻辑:
- 每次调用函数时,清除之前的定时器
- 设置新的定时器,延迟执行目标函数
- 支持立即执行选项
代码示例:
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数据类型。
核心逻辑:
- 处理基本类型和null
- 处理日期和正则对象
- 处理数组和对象
- 使用WeakMap解决循环引用
- 递归拷贝嵌套结构
代码示例:
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天前"等。
核心逻辑:
- 计算目标日期与当前日期的时间差
- 根据时间差的大小,返回不同的相对时间格式
- 支持多语言和自定义格式
代码示例:
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参数字符串。
核心逻辑:
- 解析URL中的查询字符串
- 支持数组和对象类型的参数
- 生成符合规范的查询字符串
代码示例:
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更准确。
核心逻辑:
- 利用Object.prototype.toString.call()获取准确类型
- 提取类型名称并转换为小写
- 支持所有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 typescript2. 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 build6. 发布到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等构建工具,可以创建高质量的工具库,供团队内部复用。
大厂前端团队通常会根据业务需求和技术栈,封装一系列常用的工具方法,涵盖数据处理、日期时间、性能优化、安全加密等多个方面。这些工具方法经过充分测试和实践验证,能够有效解决实际开发中的各种问题。
通过学习和借鉴大厂的实践经验,我们可以不断优化自己的工具库,提高团队的开发效率和代码质量,为项目的成功交付提供有力支持。