前端工具库,收集整理日常开发过程中的通用 utils 。
$ npm install --save @zerozhang/utils
# or
$ yarn add @zerozhang/utils
# or
$ pnpm add @zerozhang/utils
# or
$ bun add @zerozhang/utils
import { isEmpty } from '@zerozhang/utils';
console.log(isEmpty([])) // 输出 true
console.log(isEmpty([1])) // 输出 false
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[] | 参与运算的数值 |
返回一个包含 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, }
]
}
]
查找到满足条件的第一个子节点。
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']
用于处理在 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 ... }
基于原生 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()
才能继续使用。