npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

yjr-utils

v8.0.0

Published

javascript utility functions

Downloads

401

Readme

yjr-utils 在线文档 点击进入

下载安装

此包采用 ESModule 规范编写


npm i yjr-utils

pnpm i yjr-utils

yarn add yjr-utils

如何使用

全部加载

import * as yjrUtils from 'yjr-utils';

按需引入

import { fillZero, cloneDeep } from 'yjr-utils';

目录

1.单数补 0

单独补 0

import { fillZero } from 'yjr-utils';

fillZero('3'); // '03'

fillZero('13'); // '13'

支持分隔符来批量补 0

fillZero('1:5:9', ':'); // '01:05:09'

fillZero('13:6:29', ':'); // '13:06:29'

2.深拷贝

推荐使用 js 原生的深拷贝方法 structuredClone

/**
 * 如果担心兼容性可手动引入以下路径来充当垫片;
 * ( 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

// 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.数组去重 ( 浅拷贝 )

基本数据类型去重

import { toSet } from 'yjr-utils';

toSet(['str', 'str', 110, 110, true, true]);
// 运行结果
['str', 110, true];

引用数据类型去重(根据某个路径)

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.数组排序 ( 浅拷贝 )

基本数据类型

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');
// [ '园', '花', '宝', '宝' ]

引用数据类型

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.获得最大/小值 ( 浅拷贝 )

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'

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.获得时间差

传入两个时间,返回一个 [ 天,时,分,秒 ] 组成的数组

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秒

传入模板则直接返回格式化好的字符串

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.排序效仿 ( 浅拷贝 )

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.对象成员过滤(回调)

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.对象成员过滤(回调),逆向

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.防抖

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.节流

import { throttle } from 'yjr-utils';

const func = () => console.log('resizing');

window.onscroll = throttle(func, 200, {
  // 不等待第一个定时器结束,直接执行
  leading: true,
});

// 取消
window.onscroll.cancel();

// 刷新
window.onscroll.flush();

13.数组分割

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.获得一个随机数

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 种字符省略处理

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 的简化版本,直接传入一个差值时间戳

import { remainTime } from 'yjr-utils';

remainTime(7081940);
// ['00', '01', '58', '02']

remainTime(7081940, '还剩DD天HH小时mm分钟ss秒');
// '还剩00天01小时58分钟02秒'

17.监听元素是否可见

<div id="box"></div>
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(); // 停止监听

还可以设置阈值

// 阈值默认为0.5 可传0-1
const { watch, unwatch } = watchDomIsVisible(dom, handler, 0.3);

watch(); // 开始监听 元素露出30%算作可见,反之不可见

unwatch(); // 停止监听

18.获取数据类型

import { getType } from 'yjr-utils';

getType([1, 2, 3]);
// 'array'

getType({ a: 10 });
// 'object'

getType(null);
// 'null'

19.合并数据

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.判断两个变量是否相等

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 中出现但未发布的数组方法,导入该模块即可

import 'yjr-utils/src/arrayPolyfills.js';

如果是 ts 项目,则还需要在你项目中的 d.ts 文件中引入声明文件

/// <reference types="yjr-utils/src/arrayPolyfills.d.ts" />

导入以上文件后就可以在你的项目中使用这些 Api 了

[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.拉平嵌套对象 ( 浅拷贝 )

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,
  },
];
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.使平级对象嵌套 ( 浅拷贝 )

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,
          },
        ],
      },
    ],
  },
];
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.获取滚动条宽度

import { getScrollBarWidth } from 'yjr-utils';

getScrollBarWidth(); // 17

25.对象成员过滤

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 会被自动过滤掉

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.获取地址栏参数

import { getURLParameters } from 'yjr-utils';

window.location.href =
  'https://www.npmjs.com/package/yjr-utils?activeTab=versions';

getURLParameters();
// { activeTab : 'versions' };

28.货币格式化

import { currencyFormat } from 'yjr-utils';

currencyFormat(123456); // '123,456'

currencyFormat(1234567); // '1,234,567'

currencyFormat(123456.86); // '123,456.86'

29.AA 分账算法

import { aaAccountingAlgorithm } from 'yjr-utils';
const input = '小明:123; 小王:219; 小李:20.98; 小张:12; 小陈:78';
aaAccountingAlgorithm(input);

/**
 * 输出:
 * 小李 应该给 小明 转 32.4 元;
 * 小李 应该给 小王 转 37.22 元;
 * 小张 应该给 小王 转 78.6 元;
 * 小陈 应该给 小王 转 12.58 元
 * /

30.监听页面可视状态

import { pageIsVisible } from 'yjr-utils';

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

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

watch(); // 开始监听

unwatch(); // 停止监听

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

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

import { isMobile, isIOS } from 'yjr-utils';

isMobile(); // true || false

isIOS(); // true || false

33. H5 应用适配 IOS 键盘

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 依赖项记录助手

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

35. Cookie 解析器

import { cookieParse } from 'yjr-utils';

const cookie = cookieParse();

// 访问某个cookie的值
cookie.hng; // zh-cn
cookie.sid; // "89c7xw19"

36. H5 获取 ios 安全区域距离

import { getSafeArea } from 'yjr-utils';

getSafeArea().then(({ safeTop, safeBottom }) => {
  safeTop; // 47
  safeBottom; // 32
});

动画效果

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

yjrUtils 自带以下 9 种动画效果


1.fadeIn
2.fadeInLeft
3.fadeInRight
4.fadeInUp
5.flipInX
6.flipInY
7.rotateInDownLeft
8.rotateInDownRight
9.zoomIn

动画效果为可选,如需使用,引入该文件即可

import 'yjr-utils/yjrani.css';

1.通过类名使用

yjranimated 是固定前缀 动画效果为 yjr + 具体动画名

<div class="yjranimated yjrzoomIn">An animated element</div>

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

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

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

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

2.通过 @keyframes 方式使用

只需填入动画名即可使用动画效果

.className {
  animation: zoomIn 0.5s ease 0s infinite;
}

3.自定义 CSS 变量

局部变量 改变 zoomIn 这个动画持续时间为 2s

.yjranimated.yjrzoomIn {
  --animate-duration: 2s;
}

全局变量 所有动画效果都是延迟 900 毫秒且持续时间为 800 毫秒

:root {
  --animate-duration: 800ms;
  --animate-delay: 0.9s;
}

4.使用注意

为了确保动画不影响你原有的样式,建议每个动画元素都加上这句 css 代码

.demo {
  animation-fill-mode: both;
}