@zerozhang/utils v1.0.5
Utils 大全
✨ 简介
前端工具库,收集整理日常开发过程中的通用 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[] | 参与运算的数值 |
返回一个包含 result
和 next
函数的对象。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 | 必填 | unknown | catch 捕获的 error |
format | 可选 | (error: unknown) => T | 格式化非 Error 类型的错误 |
如果 error 为
Error
类型,直接返回:try { // some code } catch (error) { const { message } = handleUnknownError(error); // use message ... }
支持传入基于
Error
类扩展的自定义Error
泛型:interface HttpError extends Error { code: number; } try { // some code } catch (error) { const { message, code } = handleUnknownError<HttpError>(error); // use message and code ... }
非常规 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
吧~
创建实例
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() 文档 添加拦截器
// 添加第一个请求拦截(后执行) 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); } );
注意: 拦截器本质是
Promise
的executor
,它们都存放在一个数组中,等待 Promise 链的调用。请求拦截器相当于
unshift
到数组的前面,响应拦截器相当于push
到数组的后面。所以,后添加的请求拦截器会先执行,而后添加的响应拦截器会后执行。
使用
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' }, // ...... } );
使用原型上的
get
和post
FetchHttp 提供了快捷的get
和post
,您可以使用它们方便的发起请求。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 为例,使用局部水印功能:
实例化
<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>
更换属性
<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>
添加自适应
<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()
才能继续使用。