xl-toolbox
v1.0.1
Published
前端通用工具方法集合
Readme
xl-toolbox
前端通用工具方法集合,支持按需引入(tree-shaking),TypeScript 编写,提供完整的类型声明。
特性
- 按需引入:支持 tree-shaking,仅打包使用到的方法
- TypeScript:完整的类型声明,开发体验友好
- 双格式输出:同时支持 ESM 和 CJS
- 全面测试:154 个单元测试覆盖所有方法
安装
npm install xl-toolbox
# 或
pnpm add xl-toolbox
# 或
yarn add xl-toolbox快速开始
// 按需引入(推荐,打包时仅包含使用到的方法)
import { deepClone, debounce, formatDate } from 'xl-toolbox';
const obj = deepClone({ a: { b: 1 } });
const fn = debounce(() => console.log('执行'), 300);
const date = formatDate(new Date(), 'YYYY-MM-DD');API 文档
类型判断
提供 14 个类型判断方法,均返回布尔值,支持 TypeScript 类型守卫。
| 方法 | 说明 | 示例 |
|------|------|------|
| isObject(val) | 是否为非 null 对象 | isObject({}) // true |
| isArray(val) | 是否为数组 | isArray([]) // true |
| isString(val) | 是否为字符串 | isString('a') // true |
| isNumber(val) | 是否为有效数字(排除 NaN) | isNumber(1) // true |
| isBoolean(val) | 是否为布尔值 | isBoolean(true) // true |
| isFunction(val) | 是否为函数 | isFunction(() => {}) // true |
| isNull(val) | 是否为 null | isNull(null) // true |
| isUndefined(val) | 是否为 undefined | isUndefined(undefined) // true |
| isSymbol(val) | 是否为 Symbol | isSymbol(Symbol()) // true |
| isBigInt(val) | 是否为 BigInt | isBigInt(1n) // true |
| isDate(val) | 是否为 Date 对象 | isDate(new Date()) // true |
| isRegExp(val) | 是否为 RegExp | isRegExp(/a/) // true |
| isMap(val) | 是否为 Map | isMap(new Map()) // true |
| isSet(val) | 是否为 Set | isSet(new Set()) // true |
import { isObject, isArray, isString } from 'xl-toolbox';
isObject({}); // true
isObject(null); // false
isArray([1, 2]); // true
isString('hello'); // truedeepClone - 深拷贝
支持对象、数组、Date、RegExp、Map、Set 的深拷贝,正确处理循环引用和 Symbol 属性。
function deepClone<T>(value: T, hash?: WeakMap<object, any>): T参数
| 参数 | 类型 | 说明 |
|------|------|------|
| value | T | 待拷贝的值 |
| hash | WeakMap | 内部递归使用,处理循环引用 |
示例
import { deepClone } from 'xl-toolbox';
// 基本类型直接返回
deepClone(42); // 42
deepClone('hello'); // 'hello'
// 对象深拷贝
const obj = { a: 1, b: { c: 2 } };
const cloned = deepClone(obj);
cloned.b.c = 3;
console.log(obj.b.c); // 2(原对象不受影响)
// 处理循环引用
const cyclic: any = { a: 1 };
cyclic.self = cyclic;
deepClone(cyclic); // 正常拷贝,不栈溢出
// 支持特殊类型
deepClone(new Date()); // 新的 Date
deepClone(/abc/gi); // 新的 RegExp
deepClone(new Map([['k', 'v']])); // 新的 Map
deepClone(new Set([1, 2])); // 新的 Setdebounce - 防抖
连续调用时只在最后一次调用后的等待时间结束后执行一次,支持立即执行选项。
interface DebounceOptions {
immediate?: boolean; // 是否在调用时立即执行
}
function debounce<T extends (...args: any[]) => any>(
fn: T,
wait: number,
options?: DebounceOptions
): ((...args: Parameters<T>) => void) & { cancel: () => void }示例
import { debounce } from 'xl-toolbox';
// 默认模式:延迟执行
const search = debounce((keyword: string) => {
console.log('搜索:', keyword);
}, 300);
search('a');
search('ab');
search('abc');
// 300ms 后仅执行一次:搜索: abc
// 立即执行模式
const save = debounce(() => {
console.log('保存');
}, 1000, { immediate: true });
save(); // 立即执行
save(); // 1000ms 内忽略,结束后可再次执行
// 取消防抖
const debounced = debounce(() => {}, 300);
debounced.cancel(); // 取消未执行的调用throttle - 节流
在指定时间间隔内最多执行一次,支持首尾调用选项。
interface ThrottleOptions {
leading?: boolean; // 是否在间隔开始时执行(默认 true)
trailing?: boolean; // 是否在间隔结束时执行最后一次调用(默认 true)
}
function throttle<T extends (...args: any[]) => any>(
fn: T,
wait: number,
options?: ThrottleOptions
): ((...args: Parameters<T>) => void) & { cancel: () => void }示例
import { throttle } from 'xl-toolbox';
// 默认模式:leading + trailing
const onScroll = throttle(() => {
console.log('滚动事件');
}, 200);
window.addEventListener('scroll', onScroll);
// 仅 leading
const onClick = throttle(() => {
console.log('点击');
}, 1000, { trailing: false });
// 仅 trailing
const onResize = throttle(() => {
console.log('调整大小');
}, 200, { leading: false });
// 取消节流
const throttled = throttle(() => {}, 200);
throttled.cancel();unique - 数组去重
支持基本类型去重和基于指定字段的对象数组去重。
function unique<T>(arr: T[], key?: string): T[]示例
import { unique } from 'xl-toolbox';
// 基本类型去重
unique([1, 2, 2, 3, 3, 3]); // [1, 2, 3]
unique(['a', 'b', 'a']); // ['a', 'b']
// 对象数组按字段去重
const users = [
{ id: 1, name: 'A' },
{ id: 2, name: 'B' },
{ id: 1, name: 'C' },
];
unique(users, 'id');
// [{ id: 1, name: 'A' }, { id: 2, name: 'B' }]deepMerge - 对象深合并
递归合并多个对象,嵌套对象递归合并,数组按索引合并,返回新对象不修改原对象。
function deepMerge(...sources: Record<string, any>[]): Record<string, any>示例
import { deepMerge } from 'xl-toolbox';
// 嵌套对象合并
deepMerge(
{ a: { b: 1 } },
{ a: { c: 2 } }
);
// { a: { b: 1, c: 2 } }
// 多个对象合并
deepMerge(
{ x: 1 },
{ y: 2 },
{ z: 3 }
);
// { x: 1, y: 2, z: 3 }
// 数组按索引合并
deepMerge(
{ list: [{ a: 1 }, { b: 2 }] },
{ list: [{ c: 3 }] }
);
// { list: [{ a: 1, c: 3 }, { b: 2 }] }
// 后值覆盖前值
deepMerge(
{ name: 'A' },
{ name: 'B' }
);
// { name: 'B' }formatDate - 日期格式化
支持 Date 对象、时间戳、日期字符串输入,使用占位符格式化。
占位符
| 占位符 | 说明 |
|--------|------|
| YYYY | 年 |
| MM | 月(补零) |
| DD | 日(补零) |
| HH | 时(补零) |
| mm | 分(补零) |
| ss | 秒(补零) |
示例
import { formatDate } from 'xl-toolbox';
const date = new Date(2024, 0, 1, 8, 30, 5);
formatDate(date); // '2024-01-01 08:30:05'
formatDate(date, 'YYYY-MM-DD'); // '2024-01-01'
formatDate(date, 'YYYY/MM/DD HH:mm'); // '2024/01/01 08:30'
formatDate(date, 'YYYY年MM月DD日'); // '2024年01月01日'
// 时间戳
formatDate(1704067200000, 'YYYY-MM-DD'); // '2024-01-01'
// 日期字符串
formatDate('2024-06-15', 'YYYY/MM/DD'); // '2024/06/15'
// 无效日期
formatDate('invalid', 'YYYY-MM-DD'); // 'Invalid Date'compareVersion - 版本比较
比较两个语义化版本号,支持 v 前缀和不同位数。
function compareVersion(v1: string, v2: string): number
// 返回:1 表示 v1 > v2,-1 表示 v1 < v2,0 表示相等示例
import { compareVersion } from 'xl-toolbox';
compareVersion('1.0.0', '1.0.1'); // -1
compareVersion('1.0.0', '1.0.0'); // 0
compareVersion('2.0.0', '1.9.9'); // 1
compareVersion('v1.2.0', 'v1.1.0'); // 1
compareVersion('1.0.0', '1.0'); // 0(缺失位补 0)createVersionChecker - 版本检测器
创建版本检测器实例,支持 ETag 模式和版本文件模式双模式检测,自动轮询、页面隐藏暂停。
type VersionCheckMode = 'etag' | 'version-file';
interface VersionCheckerOptions {
mode?: VersionCheckMode; // 检测模式,默认 'etag'
url: string; // 检测 URL
versionField?: string; // 版本文件模式的字段路径,默认 'version',支持 'data.version'
currentVersion?: string; // 当前版本(版本文件模式使用)
interval?: number; // 轮询间隔(毫秒),默认 600000(10 分钟)
autoStart?: boolean; // 是否自动开始轮询,默认 true
pauseOnHidden?: boolean; // 页面隐藏时是否暂停,默认 true
onUpdate?: (info: VersionInfo) => void; // 检测到新版本时的回调
onError?: (error: Error) => void; // 检测错误的回调
fetchOptions?: RequestInit; // 自定义 fetch 请求配置
}
interface VersionInfo {
current: string; // 当前版本
latest: string; // 最新版本
hasUpdate: boolean; // 是否有更新
etag?: string | null; // ETag 值(ETag 模式)
}
interface VersionChecker {
check(): Promise<VersionInfo>; // 手动检测一次
start(): void; // 开始轮询
stop(): void; // 停止轮询
getLastResult(): VersionInfo | null; // 获取上次检测结果
destroy(): void; // 销毁实例,清理资源
}
function createVersionChecker(options: VersionCheckerOptions): VersionCheckerETag 模式(默认)
通过 HTTP 响应头 ETag 变化检测版本更新,首次检测仅记录 ETag 不触发回调。
import { createVersionChecker } from 'xl-toolbox';
const checker = createVersionChecker({
url: 'https://example.com/app.js',
interval: 10 * 60 * 1000, // 每 10 分钟检测
onUpdate: (info) => {
console.log('检测到新版本', info);
// 提示用户刷新页面
},
onError: (err) => {
console.error('检测失败', err);
},
});
// 手动检测
checker.check().then(info => {
console.log(info.hasUpdate ? '有新版本' : '已是最新');
});
// 销毁
checker.destroy();版本文件模式
通过请求 JSON 文件读取版本字段,与当前版本比较。
const checker = createVersionChecker({
mode: 'version-file',
url: 'https://example.com/version.json',
currentVersion: '1.0.0',
versionField: 'version', // 支持 'data.version' 嵌套路径
onUpdate: (info) => {
console.log(`新版本: ${info.current} -> ${info.latest}`);
},
});
// version.json 示例: { "version": "1.1.0" }
// 或嵌套结构: { "data": { "version": "1.1.0" } }
// 配置 versionField: 'data.version'页面隐藏暂停
默认开启,页面切换到后台时自动暂停轮询,返回前台时自动恢复。
const checker = createVersionChecker({
url: 'https://example.com/app.js',
pauseOnHidden: true, // 默认 true,设为 false 可禁用
});按需引入
xl-toolbox 配置了 sideEffects: false,支持 tree-shaking。使用打包工具(webpack、rollup、vite 等)时,仅引入的方法会被打包:
// 仅打包 deepClone 和 debounce,不包含其他方法
import { deepClone, debounce } from 'xl-toolbox';开发
# 安装依赖
npm install
# 构建
npm run build
# 开发模式(监听文件变化)
npm run dev
# 运行测试
npm test
# 类型检查
npm run typecheck
# 代码检查
npm run lint
# 格式化代码
npm run format技术栈
- 语言:TypeScript 5.x
- 构建:tsup 8.x(基于 esbuild)
- 测试:Vitest 1.x
- 规范:ESLint + Prettier
License
MIT
