# yjr-utils **Repository Path**: yanjingrui/yjr-utils ## Basic Information - **Project Name**: yjr-utils - **Description**: utils for js/ts - **Primary Language**: TypeScript - **License**: MIT - **Default Branch**: master - **Homepage**: https://static-mp-70f40d83-5328-46f9-b7de-0d8f6e8db731.next.bspapp.com/utils/ - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-09-23 - **Last Updated**: 2025-08-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: TypeScript, JavaScript, utils ## README **yjr-utils 在线文档 [点击进入](https://static-mp-70f40d83-5328-46f9-b7de-0d8f6e8db731.next.bspapp.com/utils/)** # 下载安装 **此包采用 ESModule 规范编写** ```js npm i yjr-utils pnpm i yjr-utils yarn add yjr-utils ``` # 如何使用 **全部加载** ```js import * as yjrUtils from 'yjr-utils'; ``` **按需引入** ```js import { fillZero, cloneDeep } from 'yjr-utils'; ``` # 目录 ## 1.单数补 0 **单独补 0** ```js import { fillZero } from 'yjr-utils'; fillZero('3'); // '03' fillZero('13'); // '13' ``` **支持分隔符来批量补 0** ```js fillZero('1:5:9', ':'); // '01:05:09' fillZero('13:6:29', ':'); // '13:06:29' ``` ## 2.深拷贝 **推荐使用 js 原生的深拷贝方法 structuredClone** ```ts /** * 如果担心兼容性可手动引入以下路径来充当垫片; * ( yjr-utils 内置了core-js ) */ import 'core-js/actual/structured-clone'; const data = [ { array: [1, 2, 3], }, ]; const data_2 = structuredClone(data); data === data_2; // false ``` **或者使用 lodash 的 cloneDeep** ```ts // yjr-utils 内部直接转发 lodash 的 cloneDeep import { cloneDeep } from 'yjr-utils'; const obj = { arr: [1, 2, 3], }; const obj_2 = cloneDeep(obj); obj === obj_2; // false ``` ## 3.数组去重 ( _浅拷贝_ ) **基本数据类型去重** ```ts import { toSet } from 'yjr-utils'; toSet(['str', 'str', 110, 110, true, true]); // 运行结果 ['str', 110, true]; ``` **引用数据类型去重(根据某个路径)** ```ts import { toSet } from 'yjr-utils'; toSet( [ { info: [180, 1] }, { info: [177, 2] }, { info: [178, 3] }, { info: [178, 3] }, { info: [171, 5] }, ], 'info[0]', ); // 运行结果 [ { info: [180, 1] }, { info: [177, 2] }, { info: [178, 3] }, { info: [171, 5] }, ]; ``` ## 4.数组排序 ( _浅拷贝_ ) **基本数据类型** ```ts import { doSort } from 'yjr-utils'; doSort([1, 7, 9, 5]); // [ 1, 5, 9, 7 ] doSort([1, 7, 9, 5], 'desc'); // [ 9, 7, 5, 1 ] doSort(['a', 'b', 'd', 'c'], 'asc', 'english'); // [ 'a', 'b', 'c', 'd' ] doSort(['a', 'b', 'd', 'c'], 'desc', 'english'); // [ 'd', 'c', 'b', 'a' ] doSort(['花', '园', '宝', '宝'], 'asc', 'chinese'); // [ '宝', '宝', '花', '园' ] doSort(['花', '园', '宝', '宝'], 'desc', 'chinese'); // [ '园', '花', '宝', '宝' ] ``` **引用数据类型** ```ts import { doSort } from 'yjr-utils'; const data = [ { a: { c: { d: 20 } }, b: 10 }, { a: { c: { d: 18 } }, b: 10 }, { a: { c: { d: 17 } }, b: 10 }, ]; doSort(data, 'asc', 'number', 'a.c.d'); // 结果 [ { a: { c: { d: 17 } }, b: 10 }, { a: { c: { d: 18 } }, b: 10 }, { a: { c: { d: 20 } }, b: 10 }, ]; ``` ## 5.获得最大/小值 ( _浅拷贝_ ) ```js import { getExtreme } from 'yjr-utils'; getExtreme([1, 2, 99]); // 99 getExtreme([1, 2, 99], 'min'); // 1 // 引用数据类型使用 const data = [ { a: { c: { d: 20 } } }, { a: { c: { d: 18 } } }, { a: { c: { d: 17 } } }, ]; getExtreme(data, 'min', 'a.c.d'); // { a: { c: { d: 17 } } } getExtreme(data, 'max', 'a.c.d'); // { a: { c: { d: 20 } } } ``` ## 6.时间格式化 **第二个参数是格式化模版,默认值是'YYYY-MM-DD HH:mm:ss'** ```ts import { timeFormat } from 'yjr-utils'; timeFormat('1999-05-12'); // '1999-05-12 08:00:00' timeFormat(1662622488019, 'YYYY年MM月DD日 HH点mm分ss秒'); // '2022年09月08日 15点34分48秒' ``` ## 7.获得时间差 **传入两个时间,返回一个 [ 天,时,分,秒 ] 组成的数组** ```ts import { timeGap } from 'yjr-utils'; timeGap('2022-09-08 07:23:46', '2022-09-05 10:43:43'); // ['02','20','40','03'] 两个时间相差2天20小时40分钟3秒 timeGap(1662521026000, 1661999899000); // ['06','00','45','26'] 两个时间相差6天0小时45分钟26秒 ``` **传入模板则直接返回格式化好的字符串** ```ts timeGap('2022-09-08 22:23:46', '2022-09-08 10:43:43', '还剩HH小时mm分钟ss秒'); // '还剩11小时40分钟03秒' timeGap('2022-09-08 13:23:46', '2022-09-05 19:54:34', 'DD天HH时mm分ss秒'); // '02天17时29分12秒' timeGap(1662521026000, 1661999899000, 'DD天HH时mm分ss秒'); // '06天00时45分26秒' ``` ## 8.排序效仿 ( _浅拷贝_ ) ```ts import { sortImitate } from 'yjr-utils'; const target = [ { type: 'A', name: '小王' }, { type: 'S', name: '小明' }, { type: 'SS', name: '小红' }, ]; const source = [ { type: 'SS', name: '小强' }, { type: 'A', name: '小刚' }, { type: 'S', name: '小金' }, ]; sortImitate(target, source, 'type'); // 结果 [ { type: 'A', name: '小刚' }, { type: 'S', name: '小金' }, { type: 'SS', name: '小强' }, ]; ``` ## 9.对象成员过滤(回调) ```js import { pickBy } from 'yjr-utils'; const obj = { a: 1, ac2: 2, a3: 3, ac4: 4, }; pickBy(obj, (val) => val >= 2); // { ac2: 2, a3: 3, ac4: 4 } pickBy(obj, (_val, key) => key.startsWith('ac')); // { ac2: 2, ac4: 4 } ``` ## 10.对象成员过滤(回调),逆向 ```js import { omitBy } from 'yjr-utils'; const obj = { a: 1, ac2: 2, a3: 3, ac4: 4, }; omitBy(obj, (val) => val >= 2); // { a: 1 } omitBy(obj, (_val, key) => key.startsWith('ac')); // { a: 1, a3: 3 } ``` ## 11.防抖 ```ts import { debounce } from 'yjr-utils'; const func = () => console.log('resizing'); window.onresize = debounce(func, 500, { // 不等待第一个定时器结束,直接执行 leading: true, // 如果一直被阻塞无法执行代码,则2秒内必定执行一次 maxWait: 2000, }); // 取消 window.onresize.cancel(); // 刷新 window.onresize.flush(); ``` ## 12.节流 ```ts import { throttle } from 'yjr-utils'; const func = () => console.log('resizing'); window.onscroll = throttle(func, 200, { // 不等待第一个定时器结束,直接执行 leading: true, }); // 取消 window.onscroll.cancel(); // 刷新 window.onscroll.flush(); ``` ## 13.数组分割 ```ts import { chunk } from 'yjr-utils'; chunk([1, 2, 3, 4], 2); // [[1,2],[3,4]] 两个成员一组来分组 chunk([1, 2, 3, 4], 3); // [[1,2,3],[4]] 三个成员一组来分组 const data = [ { a: 1, b: 1 }, { a: 2, b: 2 }, { a: 3, b: 3 }, { a: 4, b: 4 }, ]; chunk(data, 2); // 结果 [ [ { a: 1, b: 1 }, { a: 2, b: 2 }, ], [ { a: 3, b: 3 }, { a: 4, b: 4 }, ], ]; ``` ## 14.获得一个随机数 ```ts import { random } from 'yjr-utils'; random(-10, 20); // -2 random(5, 10); // 7 random(1.2, 5.2); // 3.4508875522514773 // 不返回整数 random(0, 4, true); // 3.154170094134428 random(0, 4); // 3 ``` ## 15. 文字超出省略 **支持 空格-汉字-数字-字母-特殊字符-字体图标-表情 等 7 种字符省略处理** ```ts import { textEllipsis } from 'yjr-utils'; textEllipsis('Ab&_12看看 👀🆕😊❤️', 10); // 'Ab&_12看看 👀...' textEllipsis('🉑️23', 3); // '🉑️2...' 某些非常特殊的字体图标会占用2个位置 textEllipsis('❤️ab', 3); // '❤️a...' 某些非常特殊的字体图标会占用2个位置 textEllipsis('abcdef', 4, true); // 'a...' 开启严格模式,'...'也会消费你传入的长度 ``` ## 16.剩余时间 **timeGap 的简化版本,直接传入一个差值时间戳** ```ts import { remainTime } from 'yjr-utils'; remainTime(7081940); // ['00', '01', '58', '02'] remainTime(7081940, '还剩DD天HH小时mm分钟ss秒'); // '还剩00天01小时58分钟02秒' ``` ## 17.监听元素是否可见 ```html
``` ```ts import { watchDomIsVisible } from 'yjr-utils'; const dom = document.getElementById('box'); const handler = (e) => { console.log(e ? '元素可见' : '元素不可见'); }; const { watch, unwatch } = watchDomIsVisible(dom, handler); watch(); // 开始监听 元素露出50%算作可见,反之不可见 unwatch(); // 停止监听 ``` **还可以设置阈值** ```ts // 阈值默认为0.5 可传0-1 const { watch, unwatch } = watchDomIsVisible(dom, handler, 0.3); watch(); // 开始监听 元素露出30%算作可见,反之不可见 unwatch(); // 停止监听 ``` ## 18.获取数据类型 ```ts import { getType } from 'yjr-utils'; getType([1, 2, 3]); // 'array' getType({ a: 10 }); // 'object' getType(null); // 'null' ``` ## 19.合并数据 ```js import { merge } from 'yjr-utils'; const base = { a: [{ b: 2 }, { d: 4 }], }; const other = { a: [{ c: 3 }, { e: 5 }], }; merge(base, other); // 结果 { a: [ { b: 2, c: 3 }, { d: 4, e: 5 }, ]; } ``` ## 20.判断两个变量是否相等 ```ts import { isEqual } from 'yjr-utils'; const base = { a: [1, 2, 3], }; const other = { a: [1, 2, 3], }; isEqual(base, other); // true 结构完全相同 base === other; // false 内存地址不一样 ``` ## 21.使用未发布的数组 Api **如果你想使用 findLast,findLastIndex,with,toSpliced,toReversed** **这些已经在 ESNext.d.ts 中出现但未发布的数组方法,导入该模块即可** ```ts import 'yjr-utils/src/arrayPolyfills.js'; ``` **如果是 ts 项目,则还需要在你项目中的 d.ts 文件中引入声明文件** ```ts /// ``` **导入以上文件后就可以在你的项目中使用这些 Api 了** ```ts [1, 2, 3].with(1, 90); // [1,90,3] [1, 2, 3].toReversed(); // [3,2,1] [1, 2, 3].toSpliced(1, 0, 2, 3); // [1,2,3,2,3] ``` ## 22.拉平嵌套对象 ( _浅拷贝_ ) ```ts import { flatObj } from 'yjr-utils'; const nest = [ { id: 1, pid: 0, children: [ { id: 2, pid: 1, children: [ { id: 3, pid: 2, }, ], }, ], }, ]; flatObj(nest); // 结果 [ { id: 1, pid: 0, }, { id: 2, pid: 1, }, { id: 3, pid: 2, }, ]; ``` ```js const nest2 = [ { route: 'home', pRoute: '', children: [ { route: 'home/main', pRoute: 'home', children: [ { route: 'home/main/index', pRoute: 'home/main', }, ], }, ], }, ]; flatObj(nest2); // 结果 [ { route: 'home', pRoute: '', }, { route: 'home/main', pRoute: 'home', }, { route: 'home/main/index', pRoute: 'home/main', }, ]; ``` ## 23.使平级对象嵌套 ( _浅拷贝_ ) ```ts import { nestedObj } from 'yjr-utils'; const flat = [ { id: 1, pid: 0, }, { id: 2, pid: 1, }, { id: 3, pid: 2, }, ]; nestedObj(flat, 'id', 'pid'); // 结果 [ { id: 1, pid: 0, children: [ { id: 2, pid: 1, children: [ { id: 3, pid: 2, }, ], }, ], }, ]; ``` ```js const flat2 = [ { route: 'home', pRoute: '', }, { route: 'home/main', pRoute: 'home', }, { route: 'home/main/index', pRoute: 'home/main', }, ]; nestedObj(flat, 'route', 'pRoute'); // 结果 [ { route: 'home', pRoute: '', children: [ { route: 'home/main', pRoute: 'home', children: [ { route: 'home/main/index', pRoute: 'home/main', }, ], }, ], }, ]; ``` ## 24.获取滚动条宽度 ```js import { getScrollBarWidth } from 'yjr-utils'; getScrollBarWidth(); // 17 ``` ## 25.对象成员过滤 ```js import { pick, omit } from 'yjr-utils'; const object = { a: 1, b: '2', c: 3 }; omit(object, ['a', 'c']); // { 'b': '2' } pick(object, ['a', 'c']); // { 'a': 1, 'c': 3 } ``` ## 26.对象转查询字符串 **值为 null 或 undefined 会被自动过滤掉** ```js import { queryString } from 'yjr-utils'; const obj = { number: 1, null: null, undefined: undefined, string: 'str', bool: true, }; queryString(obj); // number=1&string=str&bool=true ``` ## 27.获取地址栏参数 ```js import { getURLParameters } from 'yjr-utils'; window.location.href = 'https://www.npmjs.com/package/yjr-utils?activeTab=versions'; getURLParameters(); // { activeTab : 'versions' }; ``` ## 28.货币格式化 ```js import { currencyFormat } from 'yjr-utils'; currencyFormat(123456); // '123,456' currencyFormat(1234567); // '1,234,567' currencyFormat(123456.86); // '123,456.86' ``` ## 29.AA 分账算法 ```js import { aaAccountingAlgorithm } from 'yjr-utils'; const input = '小明:123; 小王:219; 小李:20.98; 小张:12; 小陈:78'; aaAccountingAlgorithm(input); /** * 输出: * 小李 应该给 小明 转 32.4 元; * 小李 应该给 小王 转 37.22 元; * 小张 应该给 小王 转 78.6 元; * 小陈 应该给 小王 转 12.58 元 * / ``` ## 30.监听页面可视状态 ```js import { pageIsVisible } from 'yjr-utils'; const handler = (e) => { console.log(e === 'visible' ? '页面可见' : '页面不可见'); }; const { watch, unwatch } = pageIsVisible(handler); watch(); // 开始监听 unwatch(); // 停止监听 ``` ## 31.对比两组数据间的差异 ```ts import { jsonDiff } from 'yjr-utils'; const json1 = { a: 1, b: { c: 2, d: { e: 3, kk: [1, 2, 3, { a: 33 }], }, }, }; const json2 = { a: 10, b: { c: 2, d: { f: 4, kk: [1, 2, 3, { a: 36 }], }, }, g: 5, }; jsonDiff(json1, json2); // 结果 [ { path: 'g', changeType: 'added', newValue: 5, }, { path: 'a', changeType: 'modified', oldValue: 1, newValue: 10, }, { path: 'b.d.f', changeType: 'added', newValue: 4, }, { path: 'b.d.e', changeType: 'removed', oldValue: 3, }, { path: 'b.d.kk.3.a', changeType: 'modified', oldValue: 33, newValue: 36, }, ]; ``` ## 32.是否是移动设备/是否是 IOS ```ts import { isMobile, isIOS } from 'yjr-utils'; isMobile(); // true || false isIOS(); // true || false ``` ## 33. H5 应用适配 IOS 键盘 ```tsx import { fitKeyboardInIOS } from 'yjr-utils'; /** * 如果你的H5应用被以下问题所困扰,直接引入此方法即可 * 1.ios键盘弹起导致导航栏被顶走 * 2.ios键盘切换输入法或者切到表情面板导致页面底部被盖住 */ // 在主应用节点第一次渲染完毕后获取,然后传给fitKeyboardInIOS即可
your node
; const App = document.getElementById('App'); fitKeyboardInIOS(App); ``` ## 34. useEffectPlus ```tsx import { useEffectPlus } from 'yjr-utils'; /* 继承自useEffect, * 在effect基础上会返回你是哪些依赖触发的本次更新, * 并告诉你是否是第一次执行useEffect */ function Test() { const [stateA, setStateA] = useState(0); const [stateB, setStateB] = useState(false); const [stateC, setStateC] = useState('你好'); useEffectPlus( (records) => { const [isFirstUpdate, changeIndexList] = records; if (isFirstUpdate) { // 如果是第一次执行,不运行后续逻辑 return; } if (changeIndexList.includes(0)) { // stateA 触发了本次更新 } if (changeIndexList.includes(1)) { // stateB 触发了本次更新 } if (changeIndexList.length === 3) { // stateA stateB stateC 触发了本次更新 } return () => { // 我是副作用清理函数,可选,非必填 }; }, [stateA, stateB, stateC], ); return null; } ``` ## 35. Cookie 解析器 ```js import { cookieParse } from 'yjr-utils'; const cookie = cookieParse(); // 访问某个cookie的值 cookie.hng; // zh-cn cookie.sid; // "89c7xw19" ``` ## 36. H5 获取 ios 安全区域距离 ```js import { getSafeArea } from 'yjr-utils'; getSafeArea().then(({ safeTop, safeBottom }) => { safeTop; // 47 safeBottom; // 32 }); ``` ## 37. 复制文本 ```js import { copyText } from 'yjr-utils'; copyText('要复制的文本') .then(() => { // 复制成功 }) .catch(() => { // 复制失败 }); ``` ## 动画效果 **仅 2.8.0 版本及以上支持,动画效果构建产物来自 [Animate.css](https://animate.style/#usage)** **yjrUtils 自带以下 9 种动画效果** ```js 1.fadeIn 2.fadeInLeft 3.fadeInRight 4.fadeInUp 5.flipInX 6.flipInY 7.rotateInDownLeft 8.rotateInDownRight 9.zoomIn ``` **动画效果为可选,如需使用,引入该文件即可** ```js import 'yjr-utils/yjrani.css'; ``` ### 1.通过类名使用 **yjranimated 是固定前缀 动画效果为 yjr + 具体动画名** ```html
An animated element
``` **通过添加 yjrdelay 前缀来延迟动画 比如这里延迟 2 秒触发** ```html
An animated element
``` **类似的还有控制速度和重复次数** ```html
An animated element
``` ### 2.通过 @keyframes 方式使用 **只需填入动画名即可使用动画效果** ```css .className { animation: zoomIn 0.5s ease 0s infinite; } ``` ### 3.自定义 CSS 变量 **局部变量 改变 zoomIn 这个动画持续时间为 2s** ```css .yjranimated.yjrzoomIn { --animate-duration: 2s; } ``` **全局变量 所有动画效果都是延迟 900 毫秒且持续时间为 800 毫秒** ```css :root { --animate-duration: 800ms; --animate-delay: 0.9s; } ``` ### 4.使用注意 **为了确保动画不影响你原有的样式,建议每个动画元素都加上这句 css 代码** ```css .demo { animation-fill-mode: both; } ```