Skip to content
On this page

yjr-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
<div id="box"></div>
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
/// <reference types="yjr-utils/src/arrayPolyfills.d.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.浮点型精准计算

js
import { preciseCalculation } from 'yjr-utils';

// 未处理前 0.1 + 0.2 = 0.30000000000000004
preciseCalculation(0.1, 0.2, '+'); // 0.3;

// 未处理前 0.8 - 0.1 = 0.7000000000000001
preciseCalculation(0.8, 0.1, '-'); // 0.7;

// 未处理前 0.1 * 0.2 = 0.020000000000000004
preciseCalculation(0.1, 0.2, '*'); // 0.02;

// 未处理前 0.6 / 0.2 = 2.9999999999999996
preciseCalculation(0.6, 0.2, '/'); // 3;

30.监听页面可视状态

js
import { pageIsVisible } from 'yjr-utils';

const handler = (e) => {
  console.log(e === 'visible' ? '页面可见' : '页面不可见');
};

const { watch, unwatch } = pageIsVisible(handler);

watch(); // 开始监听

unwatch(); // 停止监听

31.对比两组数据间的差异

ts
import { jsDiff } from 'yjr-utils';

const obj1 = {
  a: 100,
  b: 50,
  c: 6,
};

const obj2 = {
  a: 101,
  b: 50,
  d: 99,
};

jsDiff(obj1, obj2);

// 结果
[
  {
    path: 'a',
    desc: '修改',
    _old: 100,
    _new: 101,
  },
  {
    path: 'd',
    desc: '新增',
    _new: 99,
    _old: undefined,
  },
  {
    path: 'c',
    desc: '删除',
    _old: 6,
    _new: undefined,
  },
];

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即可
<div id="root">
  <div id="App">your node</div>
</div>;

const App = document.getElementById('App');

fitKeyboardInIOS(App);

34. useEffect 依赖项记录助手

tsx
import { depsRecorderWithUEF } from 'yjr-utils';

/** 帮助记录 useEffect 每次更新是那些依赖改变了,也可单独使用,不依赖react框架 */
function Com({ a, b, c, d, e }) {
  /**
   * 创建一个depsRecorder
   * 每个新的useEffect都要新建一个depsRecorder,depsRecorder不可共享
   */
  const depsRecorder = useMemo(() => depsRecorderWithUEF(), []);
  const depsRecorder2 = useMemo(() => depsRecorderWithUEF(), []);
  const deps = [a, b, c, d, e];
  const deps2 = [a, e];
  useEffect(() => {
    const [isFirstUpdate, changedList] = depsRecorder(deps);
    console.log(isFirstUpdate, changedList);

    if (changedList.includes(1)) {
      /**
       * 属性 b 改变时做些事情
       */
    }

    const mainArraySet = new Set([1, 2, 3]); // 代表 b c d 属性的下标
    const allItemsExist = changedList.every((item) => mainArraySet.has(item));
    if (allItemsExist) {
      /**
       * 属性 b c d 同时改变时做些事情
       */
    }

    const someItemsExist = changedList.some((item) => mainArraySet.has(item));
    if (someItemsExist) {
      /**
       * 属性 b c d 任意一个发生改变时做些事情
       */
    }

    /**
     * your other code
     */
  }, deps);

  useEffect(() => {
    const [isFirstUpdate, changedList] = depsRecorder2(deps2);

    /**
     * your other code
     */
  }, deps2);
  return <div></div>;
}

// 举例:比如你的组件更新了3次,初始化一次

// 日志: true,[0,1,2,3,4]  首次 全部更新 (a b c d e)
<Com a={'你好'} b={666} c={true} d={[1, 2, 3]} e={{ a: 'aaa', b: 'bbb' }} />;

// 日志: false,[0,3,4]   第一次 下标为 0 3 4 的依赖发生了改变 ( a d e )
<Com a={'我不好'} b={666} c={true} d={[1, 2, 3]} e={{ a: 'aaa', b: 'bbb' }} />;

const arr = [1, 1, 1];

// 日志: false,[1,2,3,4]   第二次 下标为 1 2 3 4 的依赖发生了改变 ( b c d e )
<Com a={'我不好'} b={777} c={false} d={arr} e={{ a: 'aaa', b: 'bbb' }} />;

arr[1] = 100; // 改变成员

// 日志: false,[4]   第三次 下标为 4 的依赖发生了改变 ( e )
<Com a={'我不好'} b={777} c={false} d={arr} e={{ a: 'aaa', b: 'bbb' }} />;

动画效果

仅 2.8.0 版本及以上支持,动画效果构建产物来自 Animate.css

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
<div class="yjranimated yjrzoomIn">An animated element</div>

通过添加 yjrdelay 前缀来延迟动画 比如这里延迟 2 秒触发

html
<div class="yjranimated yjrzoomIn yjrdelay-2s">An animated element</div>

类似的还有控制速度和重复次数

html
<div class="yjranimated yjrzoomIn yjrfaster yjrrepeat-3">
  An animated element
</div>

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;
}