Skip to content

Liuhuanhuanhuan/utils

 
 

Repository files navigation

Utils 大全

npm GitHub Workflow Status (with event) GitHub license Coveralls branch npm bundle size

✨ 简介

前端工具库,收集整理日常开发过程中的通用 utils 。

📦 安装

$ npm install --save @zerozhang/utils
# or
$ yarn add @zerozhang/utils
# or
$ pnpm add @zerozhang/utils
# or
$ bun add @zerozhang/utils

🔨 使用

使用 ESModule 规范导入

import { isEmpty } from '@zerozhang/utils';
console.log(isEmpty([])) // 输出 true
console.log(isEmpty([1])) // 输出 false

使用 CommonJS 规范导入

const { isEmpty } from require('@zerozhang/utils');
console.log(isEmpty([])) // 输出 true
console.log(isEmpty([1])) // 输出 false

在浏览器中使用

如果直接在浏览器中使用,则不需要包管理。直接下载 release 中的 index.umd.js,使用的是 UMD 通用模块规范。

然后在浏览器中引用:

<script src="./index.umd.js"></script>
<script>
    $utils.isNull(null) // output: true
</script>

📚 功能

浏览器

获取浏览器信息

import { getBrowser } from '@zerozhang/utils';

console.log(getBrowser()); // output: { type: 'Chrome', versions: 119 }

获取 URL 网址参数(search)或网址片段(hasn)的查询字符串。

基于 URLSearchParams 实现的处理 URL 查询字符串的方法。

import { searchParams } from '@zerozhang/utils';

// 获取 window.location.search 查询字符串
searchParams('?a=1&b=2', 'a');       // output: { a: '1' }
searchParams('?a=1&b=2', 'a','b');   // output: { a: '1', b: '2' }
searchParams('?a=1&b=2', ['a','b']); // output: { a: '1', b: '2' }
searchParams('?a=1&b=2', 'c');       // output: { c: null }

// window.location.hash 同上。

拼接 URL 网址参数(search)或网址片段(hash)

import { concatParams } from '@zerozhang/utils';

concatParams('https://localhost', { a: 1, b: 2 });
// output: https://localhost?a=1&b=2

concatParams('https://localhost', { a: 1, b: 2 }, '#');
// output: https://localhost#a=1&b=2

打开链接

import { openLink } from '@zerozhang/utils';

openLink('https://www.baidu.com'); // 默认新窗口打开

openLink('https://www.baidu.com', '_self')

文件操作

通过 url 下载文件

import { downloadByUrl } from '@zerozhang/utils';

downloadByUrl('https://www.下载地址.com', '文件名');

验证函数

  • is 基于 Object.prototype.toString 判断所有类型

  • isUnDef 验证是否是 undefined

  • isDef 验证是否已定义

  • isNull 验证是否是 null

  • isNullOrUnDef 验证是否是 null 或 undefined

  • isString 验证是否是字符串

  • isNumber 验证是否是数字

  • isBoolean 验证是否是布尔值

  • isObject 验证是否是对象

  • isArray 验证是否是数组

  • isFunction 验证是否是函数

  • isPromise 验证是否是 Promise

  • isDate 验证是否是日期

  • isRegExp 验证是否是正则表达式

  • isWindow 验证是否是 Window 对象

  • isElement 验证是否是 Element 元素

  • isHTMLElement 验证是否是 HTMLElement 元素

  • isServer 验证是否是服务端

  • isClient 验证是否是客户端

  • isEmpty 验证对象、数组、Map、Set 是否为空

      import { isEmpty } from '@zerozhang/utils';
    
      isEmpty(null);                  // true
      isEmpty(undefined);             // true
      isEmpty(123);                   // false
      isEmpty('xxx');                 // false
      isEmpty(true);                  // false
      isEmpty({});                    // true
      isEmpty([]);                    // true
      isEmpty(new Map().set('a', 1)); // false
      isEmpty(new Set().add(1));      // false

进度条

基于 NProgress 封装的进度条,可用于请求加载、路由加载等场景。

import { NProgress } from '@zerozhang/utils';

function http (url) {
    NProgress.start();

    return new Promise(() => {
        fetch(url, {/** options */})
            .then(/**  略 */)
            .catch(/** 略 */)
            .finally(() => {
                NProgress.done();
            })
    })
}

数据操作

解析 JSON

import { jsonParser } from '@zerozhang/utils';

// 标准 JOSN 格式
jsonParser('{ "name": "James", "age": 18 }');    // output: { name: 'James', age: 18 }
jsonParser('xxx');                               // output: null
jsonParser({});                                  // output: null

// 支持传入 reviver 转换器
jsonParser('{"p": 5}', (k, v) => k ? v * 2 : v); // output: { p: 10 }

封装 storage,更方便使用

 import { storageSession, storageLocal } from '@zerozhang/utils';

 storageSession.setItem('user', { name: 'James', age: 18 }); // 自动 JSON.stringify
 storageSession.getItem('user'); // 自动 JSON.parse
 storageSession.removeItem('user');
 storageSession.clear();

 // storageLocal 同理

创建 UUID

import { buildUUID, buildShortUUID } from '@zerozhang/utils';

// 生成32位的 uuid:5e4b0c9590f44163a8e76b63ae2ec00a
const uuid = buildUUID();

 // 生成24位带有默认前缀 '_' 的 uuid:_87657121411703147772655
const shortUuid1 = buildShortUUID();
const shortUuid2 = buildShortUUID('$'); // 自定义前缀

数学

用于浮点运算,解决小数点精度丢失问题

参数 是否可选 类型 说明
type 必填 字面量:+、 -、 *、 / 表示运算类型
numbers 必填 ...number[] 参与运算的数值

返回一个包含 resultnext 函数的对象。result 为计算结果,next 是一个链式调用函数,可以一直往下进行浮点运算。

import { decimalCompute } from '@zerozhang/utils';

0.1 + 0.2; // output: 0.30000000000000004 不符合预期
decimalCompute('+', 0.1, 0.2).result // output: 0.3 符合预期

0.123 * 0.3; // output: 0.036899999999999995 不符合预期
decimalCompute('*', 0.123, 0.3).result // output: 0.0369 符合预期

// next 支持链式调用
(0.1 + 0.2) * 0.12; // output: 0.036000000000000004 不符合预期
decimalCompute('+', 0.1, 0.2).next('*', 0.12).result // 0.036 符合预期

算法处理

用于处理树形结构组成的数组,一般用于嵌套菜单、树形权限等场景。数据结构类似于以下形式:

const data = [
    {
        id: '1',
        value: 1,
        children: [
            { id: '1-01', value: 12 },
            {
                id: '1-02',
                value: 13,
                children: [
                    { id: '1-02-01', value: 131 },
                    { id: '1-02-02', value: 132 },
                ]
            }
        ]
    },
    {
        id: '2',
        value: 2,
        children: [
            {
                id: '2-01',
                value: 21,
                children: [
                    { id: '2-02-01', value: 211, },
                    { id: '2-02-02', value: 212, },
                ]
            },
            { id: '2-02', value: 22, }
        ]
    }
]

查找到满足条件的第一个子节点。

import { findLatestNode } from '@zerozhang/utils';

findLatestNode(data, item => item.value % 2 === 0, 'children'); // output: { id: '1-01', value: 12 }

收集节点内满足条件的所有子节点。

import { collectLatestNodes } from '@zerozhang/utils';

collectLatestNodes(data, item => item.value % 2 === 0, 'children');
// output:
// [
  // {id: '2-02', value: 22},
  // {id: '2-02-02', value: 212},
  // {id: '1-02-02', value: 132},
  // {id: '1-01', value: 12},
// ]

根据提供的属性,收集该节点所在的树的所有层级节点的属性, 一般用于查找路径。

import { getNodesByProp } from '@zerozhang/utils';

getNodesByProp({ value: 212 }, data, 'value'); // output: ['2', '2-01', '2-02-02']

TypeScript 支持

用于处理在 TS 中使用 try catch 时,error 类型为 unknown 的场景。

参数 是否可选 类型 说明
error 必填 unknown catch 捕获的 error
format 可选 (error: unknown) => T 格式化非 Error 类型的错误
  1. 如果 error 为 Error 类型,直接返回:

    try {
        // some code
    } catch (error) {
        const { message } = handleUnknownError(error);
        // use message ...
    }
  2. 支持传入基于 Error 类扩展的自定义 Error 泛型:

    interface HttpError extends Error {
        code: number;
    }
    
    try {
        // some code
    } catch (error) {
        const { message, code } = handleUnknownError<HttpError>(error);
        // use message and code ...
    }
  3. 非常规 error(未知类型),可先自定义扩展 CustomError ,然后使用 format 函数格式化成 CustomError 需要的格式:

    // 自定义错误类
    class CustomError extends Error {
       constructor( public status: number, public statusText: string) {
           super();
       }
    }
    
    // 未知错误格式化
    function format(err: any): CustomError {
       return new CustomError(err.xxx, err.yyy)
    }
    
    try {
        // some code
    } catch (error) {
        const { status, statusText } = handleUnknownError<CustomError>(error, format);
        // use custom statusText and statusText ...
    }

HTTP 请求

基于原生 fetch 封装的类,像使用 Axios 一样去使用 fetch 吧~

  1. 创建实例

    import { FetchHttp } from '@zerozhang/utils';
    
    const http = new FetchHttp({
       mode: 'cors',
       cache: 'default',
       redirect: 'manual',
       referrer: 'client',
       credentials: 'include',
       headers: {
           'Content-Type': 'application/json',
       },
       // ...other
    });
    默认配置 说明
    headers['Accept'] 'application/json, text/plain, /' 可在实例化或使用 request 时覆盖
    headers['Content-Type'] 'application/json' 可在实例化或使用 request 时覆盖
    credentials 'include' 可在实例化或使用 request 时覆盖
    其他默认配置 与原生 fetch 相同 fetch() 文档
  2. 添加拦截器

    // 添加第一个请求拦截(后执行)
    http.interceptors.request.use(config => {
        (config.headers as Headers).set('Authorization', 'Bearer ' + 'token');
        return config;
    });
    
    // 添加第二个请求拦截(先执行)
    http.interceptors.request.use(config => {
        delete config.credentials;
        return config;
    });
    
    // 添加响应拦截
    http.interceptors.response.use(
        res => {
            // todo: 处理返回的真实数据(即解析后的 response.body)
            const data = res?.data ?? res;
            return data;
        },
        error => {
            // todo: 报错处理
            window.alert(`${error.message} (${error.code})`);
            return Promise.reject(error);
        }
    );

    注意: 拦截器本质是 Promiseexecutor,它们都存放在一个数组中,等待 Promise 链的调用。

    请求拦截器相当于 unshift 到数组的前面,响应拦截器相当于 push 到数组的后面。

    所以,后添加的请求拦截器会先执行,而后添加的响应拦截器会后执行

  3. 使用 request

    参数 是否可选 类型 说明
    url 必填 string 请求 api
    config 可选 FetchRequestConfig 见下面 FetchRequestConfig

    FetchRequestConfig 在继承于 RequestInit 类型的基础上额外支持两种属性:

    属性 是否可选 类型 说明
    params 可选 Record<string, any> 网址查询参数
    readMethod 可选 字面量 'arrayBuffer' | 'blob' | 'formData' | 'json' | 'text' 指定读取 Response 的方法,默认 json
    http.request(
       'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits',
       {
           method: 'GET',
           params: { name: 'zs', age: 18 },
           readMethod: 'text',
           headers: {
               'X-GitHub-Api-Version': '2023-12-15'
           },
           // ......
       }
    );
  4. 使用原型上的 getpost FetchHttp 提供了快捷的 getpost,您可以使用它们方便的发起请求。

    get 参数:

    参数 是否可选 类型 说明
    url 必填 string 请求 api
    params 可选 Record<string, any> 网址查询参数
    config 可选 FetchRequestConfig 见上述 FetchRequestConfig 类型

    post 参数:

    参数 是否可选 类型 说明
    url 必填 string 请求 api
    data 可选 请求实体 请求实体
    config 可选 FetchRequestConfig 见上述 FetchRequestConfig 类型
    http.get(
       'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits',
       { name: 'zs', age: 18 },
       {
           headers: { 'X-GitHub-Api-Version': '2023-12-15' },
           credentials: 'include',
       }
    )
    
    const form = new FormData();
    formData.append('file', data);
    
    http.post(
       'https://yourapi.com',
       form,
       {
           headers: { 'Content-Type': 'multipart/form-data' },
       }
    )

业务功能

支持全局、局部、颜色、旋转、阴影、渐变、透明度的多功能水印。

以 Vue 3 为例,使用局部水印功能:

  1. 实例化

    <script setup lang='ts'>
    import { ref, onMounted } from 'vue';
    import { Watermark } from '@zerozhang/utils';
        
    const watermark = ref<Watermark | null>(null);
    
    onMounted(() => {
        // 实例化
        watermark.value = new Watermark({
            title: 'hehe',
            wrapper: '#wrapper', // 局部挂载节点
            canvasAttrs: {
                width: 50,
                height: 50,
                fillStyle: 'rgba(0, 0, 0, 0.4)',
            },
        });
        // 生成图片
        watermark.set();
    });
    </script>
    
    <template>
     <div id="wrapper" />
    </template>
  2. 更换属性

    <script setup lang='ts'>
    // other code......
    
    const render = () => {
        if (watermark.value) {
            watermark.value.render({
                title: '奥利给',
                canvasAttrs: {
                     // 旋转角度
                    rotate: 0, // 默认 30
                    
                    // 阴影(shadowBlur 不为 0,才会绘制)
                    shadowBlur: 10,
                    shadowColor: 'gray', // 默认 'rgba(0, 0, 0, 0.7)'
                    shadowOffsetX: 8, // 默认 10
                    shadowOffsetY: 8, // 默认 5
                    
                    // 透明度
                    globalAlpha: 0.25, // 默认 0.7
                    
                    // 线性渐变
                    lineGradient: [
                        { value: 0, color: 'magenta' },
                        { value: 0.5, color: 'blue' },
                        { value: 1.0, color: 'red' },
                    ]
                },
            })
        }
    }
    
    const reset = () => {
        watermark.value && watermark.value.reset();
    }
    
    const clear = () => {
        watermark.value && watermark.value.clear();
    };
    </script>
    
    <template>
     <div id="wrapper" />
     <button @click="render">更换</button>
     <button @click="reset">重置</button>
     <button @click="clear">清空</button>
    </template>
  3. 添加自适应

    <script setup lang='ts'>
    import { useEventListener, useDebounceFn } from '@vueuse/core';
    
    // other code......
    
    const resize = useDebounceFn(
        () => {
            if (watermark.value) {
                watermark.value.render({
                    containerWidth: watermark.value.wrapper.clientWidth,
                    containerHeight: watermark.value.wrapper.clientHeight,
                });
            }
        },
        500,
        { maxWait: 3000 }
    );
        
    useEventListener(window, 'resize', resize);
    </script>

Watermark

参数 是否可选 类型 说明
title 必填(不填不渲染) string | number 水印标题
container 可选 string | HTMLElement | (() => HTMLElement) 水印容器,不传会内建一个 div
wrapper 可选 string | HTMLElement | (() => HTMLElement) 水印挂载节点,默认 document.body,传入字符串时,通过 document.querySelector() 获取元素
canvasAttrs 可选 CanvasAttributes canvas 属性,额外添加 lineGradient(渐变数组)、rotate(旋转角度)属性

CanvasAttributes 类型:

type CanvasAttributes = Partial<CanvasRenderingContext2D & CanvasSize> & {
  lineGradient?: Array<{ value: number; color: string }>;
  rotate?: number;
};

Watermark.set()

初始化 Watermark 后,需要调用 set() 方法生成一个水印。

Watermark.render()

水印绘制的方法。可以在自适应等需要重新渲染的场景使用。

参数 是否可选 类型 说明
title 可选 string | number 水印标题
containerWidth 可选 number 容器宽
containerHeight 可选 number 容器高
canvasAttrs 可选 CanvasAttributes 绘制 canvas 的属性
forceRender 可选 boolean 是否强制渲染 canvas,默认 false

Watermark.reset()

水印重置的方法。调用后会恢复到一开始初始化实例的样子。

Watermark.clear()

水印彻底删除的方法。调用后删除水印,移除所有配置项并将其初始化成默认值。此时,需要重新 new Watermark() 才能继续使用。

About

收集项目开发中常用的、好用的 Utils 方法

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 74.7%
  • JavaScript 24.6%
  • Shell 0.7%