Package Exports
- @gitborlando/utils
Readme
G-Utils
JavaScript/TypeScript 实用工具集合
安装
npm install @gitborlando/utils
🔧 数组工具
基础操作
import {
firstOne,
lastOne,
stableIndex,
createArray,
} from "@gitborlando/utils";
// 获取第一个元素 (支持数组和Set)
firstOne([1, 2, 3]); // 1
firstOne(new Set([1, 2, 3])); // 1
firstOne([]); // undefined
// 获取最后一个元素
lastOne([1, 2, 3]); // 3
lastOne(new Set([1, 2, 3])); // 3
lastOne([]); // undefined
// 稳定索引处理 (防止越界)
stableIndex([1, 2, 3], -1); // 0
stableIndex([1, 2, 3], 5); // 3
stableIndex([1, 2, 3], 1); // 1
stableIndex([1, 2, 3]); // 3 (返回length)
// 创建数组
createArray(5); // [0, 1, 2, 3, 4]
createArray(3); // [0, 1, 2]
高级遍历
import { loopFor, reverseFor, reverse } from "@gitborlando/utils";
// 循环遍历 (可访问当前、下一个、上一个元素)
const arr = ["A", "B", "C"];
loopFor(arr, (current, next, prev, index) => {
console.log(`索引${index}: 当前=${current}, 下一个=${next}, 上一个=${prev}`);
// 返回 true 可以跳出循环
// 返回 false 可以跳过当前迭代
});
// 反向遍历
reverseFor([1, 2, 3], (item, index) => {
console.log(`索引${index}: ${item}`);
});
// 反向数组 (不修改原数组)
const original = [1, 2, 3];
const reversed = reverse(original); // [3, 2, 1]
console.log(original); // [1, 2, 3] (原数组不变)
函数批处理
import { flushFuncs } from "@gitborlando/utils";
// 批量执行函数并清空容器
const callbacks = [
() => console.log("回调1"),
() => console.log("回调2"),
() => console.log("回调3"),
];
flushFuncs(callbacks); // 执行所有回调
console.log(callbacks.length); // 0 (数组已清空)
// 也支持Set
const callbackSet = new Set([
() => console.log("Set回调1"),
() => console.log("Set回调2"),
]);
flushFuncs(callbackSet); // 执行并清空Set
🗄️ 缓存工具
Map缓存
import { createCache } from "@gitborlando/utils";
const cache = createCache<string, number>();
// 基础操作
cache.set("key1", 100);
cache.get("key1"); // 100
cache.delete("key1");
cache.clear();
// 获取或设置 (懒加载)
const expensiveValue = cache.getSet("expensive", () => {
console.log("执行昂贵计算...");
return heavyComputation();
});
// 带比较的缓存 (依赖变更时重新计算)
const userId = 123;
const userRole = "admin";
const userPermissions = cache.getSet(
"permissions",
() => calculatePermissions(userId, userRole),
[userId, userRole], // 依赖数组
);
// 工具方法
cache.forEach((key, value, map) => {
console.log(`${key}: ${value}`);
});
cache.keys(); // IterableIterator<string>
cache.values(); // IterableIterator<number>
cache.entries(); // IterableIterator<[string, number]>
对象缓存
import { createObjCache } from "@gitborlando/utils";
const objCache = createObjCache<string>();
// 性能更好的字符串键缓存
objCache.set("name", "John");
objCache.get("name"); // 'John'
// 从对象批量设置
objCache.fromObject({
name: "John",
age: "30",
role: "admin",
});
// 转换为普通对象
const obj = objCache.toObject(); // { name: 'John', age: '30', role: 'admin' }
🛠️ 通用工具
删除操作
import { Delete } from "@gitborlando/utils";
// 删除对象属性
const obj = { a: 1, b: 2, c: 3 };
Delete(obj, "b"); // obj = { a: 1, c: 3 }
// 删除数组元素 (通过值)
const arr1 = [1, 2, 3, 2, 4];
Delete(arr1, 2); // arr1 = [1, 3, 2, 4] (删除第一个匹配)
// 删除数组元素 (通过函数)
const arr2 = [
{ id: 1, name: "John" },
{ id: 2, name: "Jane" },
{ id: 3, name: "Bob" },
];
Delete(arr2, (item) => item.id === 2); // 删除 id 为 2 的项
函数工具
import { iife, memorize, debounce } from "@gitborlando/utils";
// 立即执行函数表达式
const result = iife(() => {
const a = 1;
const b = 2;
return a + b;
}); // 3
// 函数记忆化 (缓存结果)
const fibonacci = memorize((n: number): number => {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
console.log(fibonacci(10)); // 首次计算
console.log(fibonacci(10)); // 从缓存返回
// 防抖函数
const search = debounce(300, (query: string) => {
console.log(`搜索: ${query}`);
});
search("React"); // 300ms后执行
search("React Native"); // 重置计时器
search("React Hook"); // 300ms后执行这个
条件匹配
import { matchCase, macroMatch } from "@gitborlando/utils";
// 类型安全的条件匹配
type Status = "loading" | "success" | "error";
const status: Status = "loading";
const message = matchCase(status, {
loading: "加载中...",
success: "成功!",
error: "错误!",
});
// 带默认值的匹配
const color = matchCase(status, "gray", {
loading: "blue",
success: "green",
error: "red",
});
// 宏匹配 (模板字符串)
const isValidStatus = macroMatch`loading|success|error`;
console.log(isValidStatus("loading")); // true
console.log(isValidStatus("pending")); // false
对象操作
import { clone, objKeys, useObjectKey } from "@gitborlando/utils";
// 深度克隆
const original = {
name: "John",
hobbies: ["reading", "coding"],
address: { city: "Shanghai", country: "China" },
};
const cloned = clone(original);
// 类型安全的对象键获取
interface User {
name: string;
age: number;
email?: string;
}
const userKeys = objKeys<keyof User>({ name: "John", age: 30 });
// userKeys 类型为 ('name' | 'age' | 'email')[]
// 对象唯一键 (WeakMap实现)
const obj1 = { data: "test" };
const obj2 = { data: "test" };
console.log(useObjectKey(obj1)); // 'abc123' (唯一ID)
console.log(useObjectKey(obj2)); // 'def456' (不同ID)
console.log(useObjectKey(obj1)); // 'abc123' (相同对象返回相同ID)
调试工具
import { Log, jsonFy, jsonParse } from "@gitborlando/utils";
// 链式调试日志
const result = [1, 2, 3]
.map((x) => x * 2)
.map(Log) // 打印中间结果
.filter((x) => x > 2);
// JSON 序列化 (安全版本)
const obj = { name: "John", age: 30 };
const json = jsonFy(obj); // 格式化的JSON字符串
const parsed = jsonParse(json); // 解析回对象
🎯 拖拽工具
import { DragUtil } from "@gitborlando/utils";
const drag = new DragUtil();
// 启用无限拖拽 (超出边界时重置位置)
drag.needInfinity();
// 拖拽开始 (鼠标按下)
drag.onDown((data) => {
console.log("拖拽开始:", {
start: data.start, // 起始位置 {x, y}
current: data.current, // 当前位置 {x, y}
shift: data.shift, // 偏移量 {x, y}
marquee: data.marquee, // 选择框 {x, y, width, height}
});
});
// 拖拽真正开始 (移动一定距离后)
drag.onStart((data) => {
console.log("拖拽激活:", data);
document.body.style.cursor = "grabbing";
});
// 拖拽移动中
drag.onMove((data) => {
console.log("拖拽移动:", {
current: data.current, // 当前位置
delta: data.delta, // 本次移动的增量
shift: data.shift, // 总偏移量
marquee: data.marquee, // 选择框区域
});
// 移动元素
element.style.transform = `translate(${data.shift.x}px, ${data.shift.y}px)`;
});
// 拖拽结束
drag.onDestroy((data) => {
console.log("拖拽结束:", data);
document.body.style.cursor = "default";
});
// 滑动检测 (快速拖拽)
drag.onSlide((data) => {
console.log("滑动检测:", data);
// 可以实现惯性滑动
});
🎧 事件工具
import {
listen,
isLeftMouse,
isRightMouse,
stopPropagation,
preventDefault,
} from "@gitborlando/utils";
// 事件监听 (自动返回清理函数)
const unlisten = listen("click", (e) => {
console.log("点击事件");
});
// 带选项的监听
const unlistenOnce = listen("scroll", { once: true, capture: true }, (e) => {
console.log("只执行一次的滚动事件");
});
// 鼠标按键检测
const handleMouseEvent = (e: MouseEvent) => {
if (isLeftMouse(e)) {
console.log("左键点击");
} else if (isRightMouse(e)) {
console.log("右键点击");
}
};
// 事件处理装饰器
const clickHandler = stopPropagation((e) => {
console.log("点击处理,已阻止传播");
});
const submitHandler = preventDefault((e) => {
console.log("提交处理,已阻止默认行为");
});
// 清理监听
unlisten();
unlistenOnce();
🔢 数学工具
基础数学
import {
pow2,
pow3,
multiply,
divide,
dCos,
dSin,
degreeFy,
radianFy,
} from "@gitborlando/utils";
// 平方和立方
pow2(5); // 25
pow3(3); // 27
// 数组相乘
multiply(2, 3, 4); // 24
// 安全除法 (除数为0时返回1)
divide(10, 2); // 5
divide(10, 0); // 1
// 度数三角函数
dCos(0); // 1
dCos(90); // 接近0
dSin(90); // 1
dSin(0); // 0
// 角度转换
degreeFy(Math.PI); // 180
radianFy(180); // Math.PI
几何工具
import { rotatePoint, normalAngle, numberHalfFix } from "@gitborlando/utils";
// 点旋转
const [newX, newY] = rotatePoint(
10,
10, // 要旋转的点 (ax, ay)
0,
0, // 旋转中心 (ox, oy)
45, // 旋转角度
);
// 角度标准化 (0-360度)
normalAngle(450); // 90
normalAngle(-30); // 330
// 数值修正 (处理精度问题)
numberHalfFix(0.1 + 0.2); // 0.3
📍 XY 坐标工具
基础坐标操作
import { xy_, xy_from, xy_client, xy_center } from "@gitborlando/utils";
// 创建坐标
const point = xy_(10, 20); // {x: 10, y: 20}
const origin = xy_(); // {x: 0, y: 0}
// 从对象复制
const copied = xy_from(point); // {x: 10, y: 20}
// 从鼠标事件获取
const mousePos = xy_client(mouseEvent); // {x: clientX, y: clientY}
// 从中心点获取
const centerPos = xy_center({
centerX: 100,
centerY: 200,
}); // {x: 100, y: 200}
坐标运算
import {
xy_plus,
xy_minus,
xy_multiply,
xy_divide,
xy_distance,
xy_rotate,
xy_dot,
xy_symmetric,
} from "@gitborlando/utils";
const p1 = xy_(10, 20);
const p2 = xy_(30, 40);
// 基础运算
xy_plus(p1, p2); // {x: 40, y: 60}
xy_minus(p1, p2); // {x: -20, y: -20}
xy_multiply(p1, 2, 3); // {x: 60, y: 120}
xy_divide(p1, 2); // {x: 5, y: 10}
// 几何运算
xy_distance(p1, p2); // 28.284271247461903
xy_rotate(p1, xy_(0, 0), 90); // 绕原点旋转90度
xy_dot(p1, p2); // 点积: 1100
xy_symmetric(p1, xy_(0, 0)); // 关于原点对称: {x: -10, y: -20}
XY 类
import { XY } from "@gitborlando/utils";
// 创建XY实例
const xy = new XY(10, 20);
// 链式操作
const result = xy
.plus({ x: 5, y: 5 }) // {x: 15, y: 25}
.multiply(2) // {x: 30, y: 50}
.minus({ x: 10, y: 10 }); // {x: 20, y: 40}
// 几何计算
xy.distance({ x: 0, y: 0 }); // 距离计算
xy.rotate({ x: 0, y: 0 }, 45); // 旋转
xy.angle({ x: 100, y: 100 }, { x: 0, y: 0 }); // 角度计算
// 静态方法
const fromArray = XY.FromArray([10, 20]); // new XY(10, 20)
const fromObj = XY.From({ x: 10, y: 20 }); // new XY(10, 20)
🎪 滚轮工具
import { WheelUtil } from "@gitborlando/utils";
const wheel = new WheelUtil();
// 滚轮开始
wheel.onBeforeWheel(({ e, direction }) => {
console.log("滚轮开始:", direction > 0 ? "向下" : "向上");
// 可以在这里初始化滚轮相关状态
});
// 滚轮进行中
wheel.onDuringWheel(({ e, direction }) => {
console.log("滚轮中:", direction);
// 实现滚轮缩放或滚动逻辑
if (direction > 0) {
scale *= 0.9; // 缩小
} else {
scale *= 1.1; // 放大
}
});
// 滚轮结束
wheel.onAfterWheel(({ e, direction }) => {
console.log("滚轮结束");
// 可以在这里保存状态或执行清理
});
// 绑定到DOM元素
element.addEventListener("wheel", (e) => {
e.preventDefault();
wheel.onWheel(e);
});
💾 存储工具
import { StorageUtil } from "@gitborlando/utils";
const storage = new StorageUtil();
// 存储基本类型
storage.set("name", "John");
storage.set("age", 30);
storage.set("isActive", true);
// 存储复杂对象
storage.set("user", {
id: 1,
name: "John",
preferences: {
theme: "dark",
language: "zh",
},
});
// 存储Set和Map (自动序列化)
const tags = new Set(["react", "typescript", "nodejs"]);
storage.set("tags", tags);
const userMap = new Map([
["john", { age: 30, role: "admin" }],
["jane", { age: 25, role: "user" }],
]);
storage.set("userMap", userMap);
// 读取数据 (自动反序列化)
const name = storage.get<string>("name"); // 'John'
const user = storage.get<User>("user"); // 完整的用户对象
const retrievedTags = storage.get<Set<string>>("tags"); // Set 对象
const retrievedMap = storage.get<Map<string, any>>("userMap"); // Map 对象
🚀 动画工具
import { Raf } from "@gitborlando/utils";
const raf = new Raf();
// 链式requestAnimationFrame
raf
.request((next) => {
console.log("动画帧1");
// 继续下一帧
next();
})
.request((next) => {
console.log("动画帧2");
// 条件控制
if (shouldContinue) {
next();
}
});
// 取消所有动画
raf.cancelAll();
// 使用示例:平滑动画
let progress = 0;
const animate = new Raf();
animate.request((next) => {
progress += 0.01;
element.style.opacity = progress.toString();
if (progress < 1) {
next(); // 继续动画
}
});
发布
推送代码到 main 分支时自动发布到 npm:
git add .
git commit -m "更新代码"
git push origin main
手动发布:
pnpm release