npm.io
5.0.0 • Published yesterday

@jl-org/tool

Licence
MIT
Version
5.0.0
Deps
0
Size
667 kB
Vulns
0
Weekly
0

@jl-org/tool


npm-version npm-download License typescript github

强大而实用的前端工具库,零依赖,类型完善,功能丰富

特色

  • 高性能:优化的算法和实现,如千万级函数执行也不卡顿的分时调度器
  • 零依赖:不依赖任何第三方库
  • 类型完备:完善的 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 - 强大的日期格式化,支持 LDML 标准 token(yyyy/yy/MM/M/dd/d/HH/mm/ss/SSS 等定宽与变宽写法)、单引号字面量转义、以及指定时区
  • formatTimeFromNow - 获取类似"1分钟前"的相对时间
  • dayDiff - 计算日期差值,单位(天)
  • getQuarter - 获取季度
颜色处理
数学运算
  • 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实现
网络请求工具
数据解析
  • StreamJsonParser - 流式解析JSON,适用于SSE
  • StreamXmlParser - 流式解析 XML,支持嵌套与数组,容忍不完整、畸形输入,适用于 AI 结构化流式输出
文件处理
URL处理
媒体API

查看测试用例

数据结构
事件与插件
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结合实现丝滑的滚动体验
  • 可组合 - 无限触发器,共享或独立的滚动区域
  • 调试标记 - 可选的开始/结束位置可视化标记
使用注意事项
  1. 一个触发器对应一个进度曲线 - 需要每个元素单独的进度?创建多个触发器实例
  2. 相对位置 - ['top', 'bottom']表示元素顶部对齐视口底部时进度为0
  3. 进度更新 - 当scrub=false时,触发器在进入时播放一次,离开时反向(除非once=true
  4. 动态内容 - 内容高度动态变化后记得调用ScrollTrigger.refreshAll()
  5. 性能优化 - 避免在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秒发送一次心跳
  maxQueuedMessages: 100,
  maxReconnectAttempts: 5, // 指数退避,默认最多重连5次
})

socket.onmessage = event => console.log(event.data)
socket.onclose = (event) => {
  if (!event.superseded) {
    console.log('连接已断开')
  }
}
socket.connect()

const accepted = socket.send(JSON.stringify({ type: 'message', content: 'Hello!' }))
if (!accepted) {
  /** 队列已满,或关闭队列后连接当前不可发送 */
}

socket.dispose()

WS 自身是跨底层重连保持稳定的事件目标,connect() 返回当前实例。默认开启容量为 100、TTL 为 10 秒的内存待发队列,只用于短暂断线窗口,不提供 ACK、持久化或 exactly-once 保证。不可重放的业务指令应设置 queueMessages: false 并处理 send() 的返回值或 InvalidStateError

默认不向控制台输出日志;需要诊断信息时传入 logger: console。非预期关闭会按有限指数退避重连,页面隐藏造成的暂停和显式 close() / dispose() 不会触发自动重连。手动 connect()、网络恢复 online 和页面恢复可见会开启新的重连周期。替换底层连接时始终先触发带 superseded: true 的逻辑 close,再等待新连接触发 open。显式 close() 后再次 connect() 会保留已注册的事件处理器

图片处理工具
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('这个事件只触发一次')
})
类型安全性优势
  1. 编译时检查 - TypeScript 会在编译时检查事件名称和参数类型
  2. 自动补全 - IDE 会提供事件名称和参数的自动补全
  3. 重构安全 - 重命名事件名称时,TypeScript 会确保所有引用都被更新
  4. 多种模式 - 支持字符串、枚举和严格映射三种类型模式,适应不同需求
模拟打字机效果

完整代码示例

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()
And more ...

Keywords