1.0.5 • Published 5 months ago

@zerozhang/utils v1.0.5

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

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>

📚 功能

浏览器

getBrowser

获取浏览器信息

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

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

searchParams

获取 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 同上。

concatParams

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

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

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

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

openLink

打开链接

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

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

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

文件操作

downloadByUrl

通过 url 下载文件

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

downloadByUrl('http://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();
            })
    })
}

数据操作

jsonParser

解析 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 }

SessionStorageProxy

封装 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 同理

buildUUID

创建 UUID

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

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

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

数学

decimalCompute

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

参数是否可选类型说明
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, }
        ]
    }
]

findLatestNode

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

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

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

collectLatestNodes

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

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},
// ]

getNodesByProp

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

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

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

TypeScript 支持

handleUnknownError

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

参数是否可选类型说明
error必填unknowncatch 捕获的 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 请求

FetchHttp

基于原生 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可选CanvasAttributescanvas 属性,额外添加 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() 才能继续使用。

1.0.5

5 months ago

1.0.4

5 months ago

1.0.3

5 months ago

1.0.2

5 months ago

1.0.1

6 months ago

1.0.0

6 months ago