@jl-org/tool
v3.4.4
Published
JS的瑞士军刀
Downloads
970
Readme
🛠️ @jl-org/tool
强大而实用的前端工具库,零依赖,类型完善,功能丰富
✨ 特色
- 🚀 高性能:优化的算法和实现,如千万级函数执行也不卡顿的分时调度器
- 📦 零依赖:不依赖任何第三方库
- 📐 类型完备:完善的 TypeScript 类型定义
- 🧩 模块化:按需引入,减少包体积
- 🔧 实用至上:覆盖日常开发的大部分场景
- 🌐 全面覆盖:从基础工具到高级功能,一应俱全
📥 安装
# npm
npm i @jl-org/tool
# pnpm
pnpm add @jl-org/tool
# yarn
yarn add @jl-org/tool📚 工具分类
🧰 常用工具
| 函数 | 说明 |
|------|------|
| uniqueId | 获取自增唯一ID |
| deepClone | 深拷贝,支持循环引用 |
| deepMerge | 深度合并对象,保留目标对象未包含的属性 |
| deepCompare | 深度比较两个对象是否相等,支持自定义比较规则和忽略属性 |
| wait | 等待指定时间 |
| throttle | 节流函数 |
| debounce | 防抖函数 |
| once | 限制函数调用次数 |
| isPureNum | 判断是否能强转成数字 |
| isStr | 判断是否为字符串 |
| isObj | 判断是否为对象 |
| isXXX | 更多判断... |
| parseMDCode | 解析 markdown 中的代码块 |
📊 数组处理
| 函数 | 说明 |
|------|------|
| arrToTree | 扁平数组转树形结构 |
| searchTreeData | 树形结构搜索 |
| binarySearch | 二分查找 |
| bfsFind | 广度优先遍历 |
| dfsFind | 深度优先遍历 |
| groupBy | 数组分组 |
| arrToChunk | 数组分块 |
📆 日期处理
formatDate- 强大的日期格式化formatTimeFromNow- 获取类似"1分钟前"的相对时间dayDiff- 计算日期差值,单位(天)getQuarter- 获取季度
🌈 颜色处理
mixColor- 混合两种颜色lightenColor- 调整颜色明度colorAddOpacity- 添加透明度getColorInfo- 提取颜色的RGBA值hexToRGB- 十六进制转RGBrgbToHex- RGB转十六进制
🧮 数学运算
mapRange- 将数值从一个范围映射到另一个范围calcAspectRatio- 根据面积计算宽高比clamp- 限制值在指定范围内numFixed- 解决 Number.toFixed 计算错误,精确四舍五入formatFileSize- 文件大小单位换算,支持 bit/byte/kb/mb/gb/tb 互相转换,返回包含各单位数值的对象formatDuration- 格式化时长(秒转 MM:SS 格式),支持小数秒calcCoord- 根据半径和角度获取坐标
🎨 动画处理
| 函数/类 | 说明 |
|------|------|
| ATo | 链式调用,分段执行动画 |
| ScrollTrigger | 滚动触发动画系统,实现视差等滚动动画效果 |
| SmoothScroller | 平滑滚动实现,提供惯性滚动体验 |
| createAnimation | 创建基础动画 |
| createAnimationByTime | 基于时间的动画创建器,支持对 DOM 元素和普通 JS 对象的属性进行补间动画 |
🕒 时钟与进度
Clock- 计时器,获取帧间隔、累计时间等FakeProgress- 模拟进度条,适用于未知进度的加载timer- 高级setInterval替代,使用requestAnimationFrame实现
🌐 网络请求工具
concurrentTask- 并发执行异步任务retryTask- 失败后自动重试WS- 自动重连的WebSocket
📊 数据解析
StreamJsonParser- 流式解析JSON,适用于SSEStreamSingleXmlParser- 流式解析单层 XML,适用于 AI 结构化流式输出
📄 文件处理
downloadByData/downloadByUrl- 下载文件blobToBase64/base64ToBlob- 格式转换checkFileSize- 检查文件大小convertToWav- 将 MediaRecorder/WebM/OGG 音频转换成 WAV,支持重采样和声道混合FileChunker- 文件分块处理器BinaryMetadataEncoder- 元数据与二进制数据混合编码工具createStreamDownloader- 流式下载(无内存限制)getMimeType- 获取资源的MIME类型detectFileType- 检测文件类型jsonToJsonl/jsonlToJson- JSON与JSONL格式转换readJsonlFile- 逐行读取JSONL文件appendToJsonlFile- 追加JSON数据到JSONL文件mapJsonlFile/filterJsonlFile- 对JSONL文件进行映射和过滤操作findWithJsonlFile/findIndexWithJsonlFile- 在JSONL文件中查找数据everyWithJsonlFile/someWithJsonlFile- 检查JSONL文件中数据是否满足条件
🌍 URL处理
isValidUrl- 检测链接是否合法getUrlQuery- 解析URL的查询参数getUrlPaths- 解析URL的路径部分getHostname- 获取URL的主机名getProtocol- 获取URL的协议
🎬 媒体API
Recorder- 音频录制Speaker- 语音播放SpeakToTxt- 语音转文字openCamera- 开启摄像头ScreenRecorder- 屏幕录制
📦 数据结构
🔄 事件与插件
EventBus- 消息订阅与派发Observer- 观察者模式autoUpdate- 检查页面更新
🎨 DOM与主题
| 函数 | 说明 |
|------|------|
| getCurTheme | 获取当前主题 |
| isDarkMode | 判断是否为暗色模式 |
| onChangeTheme | 监听主题变化 |
| bindWinEvent | 绑定window事件 |
| doubleKeyDown | 双击键盘事件 |
| typewriterEffect | 模拟打字机效果 |
🔧 环境变量管理 (Node.js)
| 函数 | 说明 |
|------|------|
| loadEnv | 加载环境变量文件,支持多环境自动切换 |
| getEnv | 读取环境变量,支持默认值和必需检查 |
💼 使用示例
深度操作(深拷贝、深度合并、深度比较)
import { deepClone, deepMerge, deepCompare } from '@jl-org/tool'
/** 深拷贝对象 */
const obj = { a: 1, b: { c: 2 } }
const cloned = deepClone(obj)
cloned.b.c = 3
console.log(obj.b.c) // 2 - 原对象未改变
/** 深度合并对象 */
const target = { a: 1, b: { c: 2, d: 3 } }
const source = { b: { c: 4 } }
const merged = deepMerge(target, source)
console.log(merged) // { a: 1, b: { c: 4, d: 3 } }
/** 深度比较对象 */
const obj1 = { user: { name: 'Alice', age: 30 }, tags: ['work', 'urgent'] }
const obj2 = { user: { name: 'Alice', age: 30 }, tags: ['work', 'urgent'] }
deepCompare(obj1, obj2) // true
/** 使用自定义比较规则 */
deepCompare(
{ value: 'hello' },
{ value: 'HELLO' },
{
customComparers: {
string: (a, b) => a.toLowerCase() === b.toLowerCase()
}
}
) // true(忽略大小写)
/** 忽略指定属性 */
deepCompare(
{ name: 'Alice', id: 1, timestamp: Date.now() },
{ name: 'Alice', id: 2, timestamp: Date.now() + 1000 },
{ ignores: ['id', 'timestamp'] }
) // true(忽略 id 和 timestamp)主题色自动适配
import { isDarkMode, onChangeTheme } from '@jl-org/tool'
/** 检查当前是否暗色模式 */
if (isDarkMode()) {
applyDarkTheme()
}
else {
applyLightTheme()
}
/** 监听系统主题变化 */
onChangeTheme(
() => applyLightTheme(), // 切换到亮色时
() => applyDarkTheme() // 切换到暗色时
)图片压缩和调整大小
import { compressImg, resizeImg } from '@jl-org/tool'
async function processImage(file) {
/** 压缩图片,转为webp格式 */
const compressed = await compressImg(file, 'blob', 0.8, 'image/webp')
/** 调整图片尺寸,保持比例 */
const resized = await resizeImg(compressed, 800, 600)
return resized
}🎧 WebM 音频转换为 WAV
import { convertToWav } from '@jl-org/tool'
async function toWav(webmBlob: Blob) {
const wavBlob = await convertToWav(webmBlob, {
sampleRate: 16000, // 语音识别常用采样率
channels: 1, // 混合为单声道
})
return URL.createObjectURL(wavBlob)
}
convertToWav基于浏览器的AudioContext,可在录音结束后立即完成重采样与格式化,避免后端再做二次处理。
🔄 分时渲染调度器
类似 React 调度器,在浏览器空闲时执行任务,即使是千万级函数执行也不会卡顿
import { scheduleTask } from '@jl-org/tool'
/** 处理大量任务而不阻塞主线程 */
const tasks = Array.from({ length: 10000 }, (_, i) => () =>
Promise.resolve(heavyCalculation(i)))
scheduleTask(tasks).then((results) => {
console.log('所有任务已完成!')
})🎨 动画处理
import { ATo, createAnimationByTime } from '@jl-org/tool'
/**
* 过渡到 to 的样式属性
*/
createAnimationByTime({
target: document.querySelector('.yourSelector'),
to: { x: 200, opacity: 0.3 },
duration: 1000,
})
/**
* 分段处理,链式调用
* - 先执行 .yourSelector1 的动画
* - 再执行 .yourSelector2 的动画
*/
const ato = new ATo()
ato
.start({
target: document.querySelector('.yourSelector1'),
to: { x: 100, rotate: 360 },
duration: 1000,
ease: 'easeInOut',
})
.next({
target: document.querySelector('.yourSelector2'),
to: { x: 250, scale: 1.2, rotate: -360 },
duration: 1000,
ease: 'easeInOut',
})📜 滚动触发动画
强大的滚动动画系统,类似GSAP的ScrollTrigger,实现视差效果、滚动进度指示器等
import { ScrollTrigger } from '@jl-org/tool'
/**
* 基于滚动的动画基础示例
*/
new ScrollTrigger({
trigger: '.hero', // 控制进度的元素
targets: '.hero__img', // 要动画的元素
start: ['top', 'bottom'], // 当.hero顶部碰到视口底部时
end: ['bottom', 'top'], // 当.hero底部碰到视口顶部时
scrub: true, // 将进度直接绑定到滚动位置
smoothScroll: true, // 启用平滑滚动
props: [ // 从起始值到结束值的样式
{ scale: 1, opacity: 1 }, // 开始状态
{ scale: 1.3, opacity: 0 } // 结束状态
],
})ScrollTrigger 主要特性
- 📏 声明式API - 描述元素何时进入视口以及应该发生什么
- 🔄 惯性滚动 - 与
SmoothScroller结合实现丝滑的滚动体验 - 🧩 可组合 - 无限触发器,共享或独立的滚动区域
- 🔍 调试标记 - 可选的开始/结束位置可视化标记
使用注意事项
- 一个触发器对应一个进度曲线 - 需要每个元素单独的进度?创建多个触发器实例
- 相对位置 -
['top', 'bottom']表示元素顶部对齐视口底部时进度为0 - 进度更新 - 当
scrub=false时,触发器在进入时播放一次,离开时反向(除非once=true) - 动态内容 - 内容高度动态变化后记得调用
ScrollTrigger.refreshAll() - 性能优化 - 避免在
onUpdate回调中进行繁重的DOM操作,尽量缓存查询结果
多区域视差效果示例
/** 为每一个 section 单独创建 ScrollTrigger */
document.querySelectorAll<HTMLElement>('section').forEach((sec, i) => {
new ScrollTrigger({
trigger: sec, // 关键:把 trigger 指向该 section
targets: sec, // 该 section 自己
scrub: true,
smoothScroll: true,
start: ['top', 'bottom'],
end: ['bottom', 'top'],
props: [
{ backgroundPositionY: `-${height / 2}px` },
{ backgroundPositionY: `${height / 2}px` },
],
})
})📡 自动重连的 WebSocket
import { WS } from '@jl-org/tool'
const socket = new WS({
url: 'wss://example.com/socket',
heartbeatInterval: 3000, // 每3秒发送一次心跳
})
socket.connect()
socket.send(JSON.stringify({ type: 'message', content: 'Hello!' }))🖼️ 图片处理工具
import { compressImg, cutImg, resizeImg } from '@jl-org/tool'
/** 压缩图片 */
const compressed = await compressImg(imageEl, 'base64', 0.7)
/** 缩放图片 */
const resized = await resizeImg(imageEl, 800, 600)
/** 裁剪图片 */
const cropped = await cutImg(imageEl, { x: 10, y: 10, width: 200, height: 200 })📊 事件系统
EventBus 提供了强大的类型安全性,支持多种泛型参数类型,包括字符串、枚举和对象映射类型:
import { EventBus } from '@jl-org/tool'
// 1. 基础字符串类型事件(默认)
const basicBus = new EventBus()
// 2. 枚举类型事件(推荐,提供最佳类型安全性)
enum AppEvents {
DataUpdate = 'data-update',
UserLogin = 'user-login'
}
const enumBus = new EventBus<AppEvents>()
// 3. 对象映射类型事件(最严格的类型检查)
interface EventMap {
'user-action': { action: string, userId: string }
'data-loaded': { data: any[], timestamp: number }
}
const strictBus = new EventBus<EventMap>()
/** 使用示例 */
const bus = new EventBus({
/**
* ## 是否触发遗漏的事件
* 当尚未 on 监听事件前发送的事件,会存起来会在监听时执行
*/
triggerBefore: true
})
/** 订阅事件 */
bus.on('dataChange', (data) => {
console.log('数据变化:', data)
})
/** 发送事件 */
bus.emit('dataChange', { value: 'new value' })
/** 一次性订阅 */
bus.once('singleEvent', () => {
console.log('这个事件只触发一次')
})类型安全性优势
- 编译时检查 - TypeScript 会在编译时检查事件名称和参数类型
- 自动补全 - IDE 会提供事件名称和参数的自动补全
- 重构安全 - 重命名事件名称时,TypeScript 会确保所有引用都被更新
- 多种模式 - 支持字符串、枚举和严格映射三种类型模式,适应不同需求
📠 模拟打字机效果
import { typewriterEffect } from '@jl-org/tool'
typewriterEffect({
content: '这是将要逐字显示的文本内容...',
speed: 50, // 打字速度 (ms)
onUpdate: (text) => {
/** 将更新后的文本应用到你的DOM元素上 */
document.getElementById('my-element').textContent = text
},
})🗄️ LRU缓存
import { LRUCache } from '@jl-org/tool'
/** 创建容量为100的LRU缓存 */
const cache = new LRUCache<string, any>(100)
/** 设置缓存 */
cache.set('user:1', { name: 'John', age: 30 })
/** 获取缓存,会自动更新使用顺序 */
const user = cache.get('user:1')📊 元数据与二进制混合编码
import { BinaryMetadataEncoder } from '@jl-org/tool'
/** 创建元数据和二进制数据 */
const metadata = { name: 'image.png', type: 'image/png', size: 1024 * 50 }
const binaryData = new Uint8Array([/* 图片数据 */]).buffer
/** 编码:合并元数据和二进制数据为单一 ArrayBuffer */
const encoded = BinaryMetadataEncoder.encode(metadata, binaryData)
/** 解码:提取元数据和原始二进制数据 */
const { metadata: extractedMeta, buffer: originalBuffer }
= BinaryMetadataEncoder.decode<typeof metadata>(encoded)
console.log(extractedMeta) // { name: 'image.png', type: 'image/png', size: 51200 }⏱️ 时钟与计时
import { Clock } from '@jl-org/tool'
/** 创建时钟实例 */
const clock = new Clock()
function animate() {
/** 获取两帧之间的时间间隔 */
console.log('帧间隔(秒):', clock.delta)
console.log('帧间隔(毫秒):', clock.deltaMS)
/** 获取经过的总时间 */
console.log('总时间(秒):', clock.elapsed)
requestAnimationFrame(animate)
}
animate()