iota-utools
v0.1.5
Published
一个功能丰富、类型安全的 JavaScript/TypeScript 工具库,专为现代 Web 开发而设计。我精心打造了这套工具集,旨在提供高效、可靠且易用的解决方案。
Readme
IOTA Utils
一个功能丰富、类型安全的 JavaScript/TypeScript 工具库,专为现代 Web 开发而设计。我精心打造了这套工具集,旨在提供高效、可靠且易用的解决方案。
核心特性
- 🚀 高性能:优化的算法和数据结构
- 🔒 类型安全:完整的 TypeScript 支持
- 📦 模块化:按需导入,减少打包体积
- 🧪 测试覆盖:全面的单元测试
- 📚 文档完善:详细的使用说明和示例
安装
npm install iota-utils
# 或
yarn add iota-utils
# 或
pnpm add iota-utils快速开始
import { getTypeof, setLocalStorage, catchError } from "iota-utils";
// 类型判断
console.log(getTypeof([])); // 'array'
// 存储操作
setLocalStorage("user", { name: "John" });
// 错误处理
const [err, data] = await catchError(async () => {
return await fetch("/api/data").then((r) => r.json());
});功能模块详解
🔍 类型判断 (Type Utilities)
强大的类型检测和判断工具,支持所有 JavaScript 数据类型。
import {
getTypeof,
isArray,
isBoolean,
isString,
isNumber,
isObject,
isFunction,
isMap,
isSet,
isSymbol,
} from "iota-utils";
// 获取精确类型名称
getTypeof([]); // 'array'
getTypeof({}); // 'object'
getTypeof(""); // 'string'
getTypeof(42); // 'number'
getTypeof(true); // 'boolean'
getTypeof(() => {}); // 'function'
getTypeof(null); // 'null'
getTypeof(undefined); // 'undefined'
// 类型守卫函数
if (isArray(value)) {
value.forEach((item) => console.log(item));
}
if (isObject(data)) {
Object.keys(data).forEach((key) => {
console.log(`${key}: ${data[key]}`);
});
}最佳实践:
- 在运行时类型检查时使用
is*函数 getTypeof适合调试和日志记录- 结合 TypeScript 的类型守卫使用
💾 存储操作 (Storage)
完整的浏览器存储解决方案,支持监听和批量操作。
基础存储操作
import {
setLocalStorage,
getLocalStorage,
removeLocalStorageKey,
setSessionStorage,
getSessionStorage,
removeSessionStorageKey,
} from "iota-utils";
// LocalStorage 操作
setLocalStorage("user", { id: 1, name: "John", email: "[email protected]" });
const user = getLocalStorage("user"); // 自动反序列化
removeLocalStorageKey("user");
// SessionStorage 操作
setSessionStorage("tempToken", "abc123");
const token = getSessionStorage("tempToken");
removeSessionStorageKey("tempToken");
// 批量删除
removeLocalStorageKey("key1", "key2", "key3");存储监听
import { monitorStorage } from "iota-utils";
interface StorageEvent {
key: string;
newValue: any;
oldValue: any;
storageArea: "localStorage" | "sessionStorage";
}
// 开始监听
const cleanup = monitorStorage({
type: "both", // 'local' | 'session' | 'both'
emitUnchanged: false, // 值未变化时是否触发
onError: (error) => console.error("Storage monitoring error:", error),
});
// 监听事件
window.addEventListener("local", (e: CustomEvent<StorageEvent>) => {
console.log("Local storage changed:", e.detail);
});
window.addEventListener("session", (e: CustomEvent<StorageEvent>) => {
console.log("Session storage changed:", e.detail);
});
// 停止监听
cleanup();注意事项:
- 存储的数据会自动 JSON 序列化/反序列化
- 监听器会自动处理跨标签页同步
- 错误处理确保存储操作的稳定性
📁 文件操作 (File Operations)
文件下载和上传的便捷解决方案。
import { downFile, downStream } from "iota-utils";
// 下载文本文件
downFile("Hello, World!", "greeting.txt", "text/plain;charset=utf-8");
// 下载二进制数据
const blob = new Blob(["binary data"], { type: "application/octet-stream" });
downFile(blob, "data.bin");
// 下载远程文件流
await downStream(
"https://api.example.com/files/document.pdf",
"document.pdf",
"Bearer your-token-here",
{ userId: 123, format: "pdf" },
);📊 数组处理 (Array Utilities)
高效的数组操作和转换工具。
import { uniqueArrayByProperty, arraySlice } from "iota-utils";
// 对象数组去重
const users = [
{ id: 1, name: "John", department: "IT" },
{ id: 2, name: "Jane", department: "HR" },
{ id: 1, name: "John", department: "IT" }, // 重复
{ id: 3, name: "Bob", department: "Finance" },
];
const uniqueUsers = uniqueArrayByProperty(users, "id");
// 结果: 移除了重复的 id: 1
// 数组分片
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const chunks = arraySlice(numbers, 3);
// 结果: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]🏗️ 对象处理 (Object Utilities)
对象扁平化和还原的强大工具。
import { flattenObject, restoreObject } from "iota-utils";
// 深度扁平化
const nested = {
user: {
profile: {
name: "John",
age: 30,
address: {
street: "123 Main St",
city: "Anytown",
coordinates: {
lat: 40.7128,
lng: -74.006,
},
},
},
preferences: {
theme: "dark",
notifications: true,
},
},
metadata: {
version: "1.0",
timestamp: Date.now(),
},
};
const flat = flattenObject(nested);
// 结果:
// {
// 'user.profile.name': 'John',
// 'user.profile.age': 30,
// 'user.profile.address.street': '123 Main St',
// 'user.profile.address.city': 'Anytown',
// 'user.profile.address.coordinates.lat': 40.7128,
// 'user.profile.address.coordinates.lng': -74.0060,
// 'user.preferences.theme': 'dark',
// 'user.preferences.notifications': true,
// 'metadata.version': '1.0',
// 'metadata.timestamp': 1640995200000
// }
// 还原对象结构
const restored = restoreObject(flat);
// 结果与原始 nested 对象完全相同🧵 字符串处理 (String Utilities)
字符串格式化和模板处理的实用工具。
import { format, parmas } from "iota-utils";
// 字符串格式化(类似 C# String.Format)
const greeting = format("Hello, {0}! Welcome to {1}.", "John", "our app");
// 结果: 'Hello, John! Welcome to our app.'
const message = format(
"User {0} scored {1} points in {2}.",
"Alice",
95,
"Math",
);
// 结果: 'User Alice scored 95 points in Math.'
// 查询字符串处理
const queryString = "name=John+Doe&age=30&city=New+York&active=true";
const params = parmas(queryString);
// 结果: { name: 'John Doe', age: '30', city: 'New York', active: 'true' }
// 对象转查询字符串
const obj = { name: "John Doe", age: 30, city: "New York" };
const query = parmas(obj); // 注意:这里可能需要 encodeParams 函数,文档中是 parmas
// 假设有 encodeParams: 'name=John+Doe&age=30&city=New+York'🌳 树形结构处理 (Tree Utilities)
完整的树形数据结构操作工具集。
import {
toFlatTree,
toTreeFlat,
deepClone,
deleteTreeNode,
findTreeNode,
fuzzySearchTree,
TreeNode,
} from "iota-utils";
// 定义树节点接口
interface MyTreeNode {
id: number;
title: string;
children?: MyTreeNode[];
expanded?: boolean;
}
// 示例树结构
const tree: MyTreeNode = {
id: 1,
title: "Root",
children: [
{
id: 2,
title: "Branch A",
children: [
{ id: 4, title: "Leaf 1" },
{ id: 5, title: "Leaf 2" },
],
},
{
id: 3,
title: "Branch B",
children: [{ id: 6, title: "Leaf 3" }],
},
],
};
// 树转扁平结构
const flat = toFlatTree(tree);
// 结果: 包含所有节点及其层级信息的数组
// 扁平结构转树
const restoredTree = toTreeFlat(flat);
// 深拷贝(完整克隆树结构)
const clonedTree = deepClone(tree);
// 删除节点及其子节点
const newTree = deleteTreeNode(tree, 2); // 删除 id 为 2 的节点
// 查找节点
const foundNode = findTreeNode(tree, 5, "id");
// 返回 id 为 5 的节点
// 模糊搜索
const searchResults = fuzzySearchTree(tree, "Leaf", "title");
// 返回所有 title 包含 'Leaf' 的节点⚡ 异步处理 (Async Utilities)
Go 风格的错误处理,让异步代码更清晰。
import { catchError, wrapCatch } from "iota-utils";
// 直接错误捕获
async function fetchUserData(userId: number) {
const [err, data] = await catchError(async () => {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
});
if (err) {
console.error("Failed to fetch user:", err);
return null;
}
return data;
}
// 包装函数使用
const safeFetchUser = wrapCatch(async (userId: number) => {
const response = await fetch(`/api/users/${userId}`);
return await response.json();
});
// 使用包装后的函数
const [err, user] = await safeFetchUser(123);
if (err) {
console.error("Error:", err);
} else {
console.log("User:", user);
}
// 同步函数也可以包装
const safeParseJson = wrapCatch((jsonString: string) => {
return JSON.parse(jsonString);
});
const [parseErr, parsedData] = safeParseJson('{"name": "John"}');⬆️ 文件上传 (Upload Utilities)
大文件切片上传,支持进度回调和断点续传。
import { uploadSlice } from "iota-utils";
async function uploadLargeFile(file: File) {
const CHUNK_SIZE = 1024 * 1024; // 1MB
const UPLOAD_URL = "https://api.example.com/upload";
try {
const result = await uploadSlice(
file,
CHUNK_SIZE,
UPLOAD_URL,
(progress: number) => {
console.log(`Upload progress: ${progress}%`);
},
{
headers: {
Authorization: "Bearer token123",
"X-File-Name": file.name,
},
// 其他配置...
},
);
if (result.finish) {
console.log("Upload completed successfully!");
} else {
console.log("Some chunks failed:", result.failedSlices);
// 可以在这里实现重试逻辑
}
} catch (error) {
console.error("Upload failed:", error);
}
}🆔 UUID 生成 (UUID Utilities)
灵活的唯一标识符生成工具。
import { generateUUID, generateString } from "iota-utils";
// 生成标准 UUID v4
const uuid = generateUUID();
// 示例: "550e8400-e29b-41d4-a716-446655440000"
// 使用自定义模板
const customId = generateUUID("xxxx-yyyy-zzzz");
// 示例: "a1b2-c3d4-e5f6"
// 生成随机字符串(字母开头)
const randomStr = generateString(); // 默认 8 位
// 示例: "Ak7x9Pq2"
const longStr = generateString(16);
// 示例: "Bw3mK9nX5vY2pQ8r"
// 生成指定长度的随机字符串
const shortId = generateString(4);
// 示例: "X9k2"📅 日期处理 (Date Utilities)
日历生成和日期操作工具。
import { generateCalendar } from "iota-utils";
interface CalendarDay {
date: Date;
meta: {
year: number;
month: number; // 1-12
day: number; // 1-31
dayOfWeek: string;
type: "prev" | "current" | "next";
formattedDate: string;
};
}
// 生成指定年月的完整日历数据
const calendar: CalendarDay[] = generateCalendar(2024, 3); // 2024年3月
// 日历数据包含:
// - 上个月的尾部日期(用于填充日历网格)
// - 当月的完整日期
// - 下个月的开头日期(用于填充日历网格)
// 使用示例:渲染日历组件
calendar.forEach((day) => {
const { date, meta } = day;
if (meta.type === "current") {
console.log(`${meta.formattedDate}: ${meta.dayOfWeek}`);
}
});🛠️ 通用工具 (General Utilities)
防抖、节流、复制文本、延时等常用工具函数。
import { debounce, throttle, copyText, sleep } from "iota-utils";
// 防抖函数
const debouncedSearch = debounce((query: string) => {
console.log("Searching for:", query);
// 执行搜索逻辑...
}, 300);
// 使用防抖搜索
debouncedSearch("hello");
debouncedSearch("hello world"); // 只有最后一次调用会执行
// 节流函数
const throttledScroll = throttle(() => {
console.log("Scroll event");
}, 100);
// 窗口滚动事件
window.addEventListener("scroll", throttledScroll);
// 复制文本到剪贴板
await copyText("Hello, World!");
// 延时执行
await sleep(1000); // 暂停 1 秒
console.log("1 second later...");
// 组合使用:带防抖的异步操作
const debouncedAsyncSave = debounce(async (data: any) => {
const [err] = await catchError(async () => {
await saveToServer(data);
});
if (err) {
console.error("Save failed:", err);
}
}, 500);类型定义
// 错误结果类型
export type ErrorResult<T> = [Error | null, T | undefined];
// 树节点接口
export interface TreeNode {
id: string | number;
children?: TreeNode[];
[key: string]: any;
}
// 存储监听配置
export interface MonitorStorageOptions {
type: "local" | "session" | "both";
emitUnchanged?: boolean;
onError?: (error: Error) => void;
}
// 文件上传配置
export interface UploadConfig {
headers?: Record<string, string>;
timeout?: number;
retries?: number;
[key: string]: any;
}最佳实践
1. 错误处理
// ✅ 推荐:使用 catchError 进行统一错误处理
const [err, data] = await catchError(async () => {
return await apiCall();
});
if (err) {
handleError(err);
return;
}
processData(data);
// ❌ 避免:传统的 try-catch
try {
const data = await apiCall();
processData(data);
} catch (err) {
handleError(err);
}2. 类型安全
// ✅ 利用 TypeScript 类型推断
const [err, user] = await catchError(fetchUser);
if (err) return;
// user 的类型被正确推断,无需额外类型断言3. 性能优化
// ✅ 合理使用防抖和节流
const debouncedSearch = debounce(searchAPI, 300);
const throttledResize = throttle(handleResize, 100);
// ✅ 批量存储操作
setLocalStorage("batchData", largeObject);4. 内存管理
// ✅ 及时清理监听器
const cleanup = monitorStorage({ type: "both" });
// ... 使用中 ...
cleanup(); // 清理以防止内存泄漏浏览器兼容性
- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+
许可证
MIT License - 详见 LICENSE 文件
发布与版本管理
项目自带一个辅助脚本 npm run release(执行 node scripts/publish.js),用于自动:
- 询问或自动计算下一个语义版本号
- 运行生产构建
- 提交
package.json修改并打 Git 标签 - 推送代码和标签到远程仓库
- 发布到 npm
脚本会在开始前检查工作区是否干净,失败时会回滚版本号。它还会自动检测已有的 Git 标签,如果用户输入的版本已经被使用,会自动递增直到找到可用的版本号并给出提示。
⚠️ 如果你手动创建了标签(例如
v0.1.6),再运行npm run release时脚本会跳过已有版本并继续到下一个可用版本,避免冲突。
需要手动删除旧标签时可以执行:
# 删除本地标签
git tag -d v0.1.6
# 同步删除远程标签
git push --delete origin v0.1.6贡献
欢迎提交 Issue 和 Pull Request!在贡献代码前,请确保:
- 所有测试通过:
npm test - 代码格式化:
npm run lint - 类型检查:
npm run type-check
