前言
记录自己日常开发中,常用到的一些工具函数,如果有问题欢迎大家指正,谢谢~
1. 根据传入的对象构造url
/**
* 可以根据传递的对象构造url 常用在get请求或者链接跳转
* 例如 user/userList {id:"123",name:"小王"}
* 返回 user/userList?id=123&name=小王
*/
export function createSearch(url, params) {
const query = Object.keys(params).reduce((acc, cur) => {
debugger
if (params[cur]) {
if (!acc) return `?${acc}=${params[cur]}`;
return `${acc}&${cur}=${params[cur]}`
}
return acc
}, '')
return `${url}${query}`
}
2. JSON格式容错判断
/**
* JSON在 JSON.parse 的时候,在参数为 undefined、null 或者不合规时,会报错
* 所以在 JSON.parse 时候应该进行容错处理 可以简单封装成一个函数,放在项目中特定的文件中进行维护
* 对异常进行捕获
*/
export const parse = <T = any> (text: string,func?: (test: T) => any ) => {
let parsedString: strng | T = "";
try {
parsedString = JSON.parse(text);
} catch (error) {
parsedString = text;
}
if(typeof func === "function") return func(parsedString as T);
return parsedString;
}
3. 一维数组去重
/**
* 一维数组或者一维对象数组去重
* @params arr 要去重的数据
* @params key 去重字段
*/
export const removeDuplicates = function (arr: any[], key?: string): any[] {
if (key) {
const map = new Map();
return arr.filter((item) => !map.has(item[key]) && map.set(item[key], 1))
} else {
return [... new Set(arr)]
}
}
// [{name:"123"},{name:"456"},{name:"123"}] => [{name:"123"},{name:"456"}]
4. 解析url中的参数
/**
* 解析url参数
* @params url 要解析的参数
*/
export const getSearcParamsUtil = function (url: string): Record<string, any> {
const result = {}
// 取url中的参数部分
const str = url.substr(url.indexOf('?') + 1)
// 将参数部分按照 & 分割为数组
const paramsArr = str.split('&')
paramsArr.forEach(item => {
const params = item.split('=');
result[params[0]] = params[1];
});
return result
}
5. 解析带有HTML实体的字符串
/**
* 在某些情况下,后端在入库存储时可能会将HTML实体,例如:换行、引号等进行转义
* 但是在读库的时候又没有进行反转义,这时候在回填的时候可能会出现文本问题
*
* @params str
*/
export const decodeHtmlEntities = (str?: string) {
if(!str) return "";
else{
let result = "";
const span = document.createElement("span");
span.innerHTML = str;
span.childNodes?.forEach((v)=>result += v?.nodeValue);
return result;
}
}
6. 使用 a 标签下载文件
HTML <a> 元素可以创建一个到其他网页、文件、同一页面内的位置、电子邮件地址或任何其他URL的超链接。
<a>元素的 href 和 download 是我们下载文件所需要的两个属性。
download 可以实现对下载文件的重命名,但目前存在很大的兼容性问题,只有Chrome和Opear才有效,并且如果说下载文件不是在子集的服务器或域名中(),这些浏览器会忽视download属性,文件名不变。简而言之,跨域会导致download属性无效。另外再说一下 target 属性
target _blank打开新页面 _self在本身页面中打开
// GET 下载文件
const download = (path:sring, target?: "_blank" | "_self") => {
const a = document.createElement("a");
a.download = "";
a.target = target ?? "_self";
a.href = path;
a.style.display = "none";
// 向body的子节点末尾添加上面的 a 标签
document.body.appendChild(a);
a.click();
// 触发下载之后,移除 a 标签
document.body.removeChild(a);
}
/***
* GET POST 使用Blob(二进制流)下载文件
*
* 正常情况下 axaios 或者request 不会处理二进制数据,即:会去接收但是不会去处理,需要在请求的时候设置 responseType: "blob"
* 拿到文件流之后,这时候文件流就会被保存在内存里。需要我们生成一个 URL 来实现下载,,而生成的URL 就是文件流在内存中的地址(这个地址为临时的,浏览器在 document 卸载时,会自动释放它们)
* 我们可以通过 URL.createObjectURL() 方法生成一个链接
*
* 这里用了Blob对象,该方法的意思是用从服务器接收到的文件流创建了一个blob对象,
* 并使用该blob创建一个指向类型数组的URL,将该url作为a标签的链接目标,然后去触发a标签的点击事件从而实现下载功能。
*
* @ requestFn 文件导出的异步请求
* @ fileName 文件名称
*/
const downloadBlod = async (requestFn, fileName) => {
const { data, response } = await requestFn();
const contentType = response.headers.get("Content-type")?.split(";")?.[0]; //获取请求的类型
let reFileName = window.decodeURI(response.headers.get("Content-Disposition")?.split("=")?.[0]); //获取请求文件名称
if (["undefined", "null"].includes(reFileName)) reFileName = fileName;
if (contentType === "application/json") return false; // 接口未正常导出文件时
// 接口正常导出文件时 处理二进制文件流
if (contentType === "application/octet-stream") { // 非IE下载
const blob = new Blob([data], { type })
if ("download" in document.createElement("a")) {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.setAttribute('download', reFileName);// a标签的 download 属性可以指定文件的名称
document.body.appendChild(a);
a.click();
document.body.removeChild(a); //移除 a 标签
URL.revokeObjectURL(url); //释放URL对象
} else window.navigator.msSaveBlob(blob, reFileName); //IE下载
return true;
}
return false;
}
// 使用
document.getElementById('down').onclick = async () => {
downloadBlod(
() => { exportRequest({ responseType: "blob", getResponse: true }) }, // 导出的异步请求
"文件导出.xls"
)
}
7. 递归获取对象的key,如果对象不存在某个属性或属性值未获取到则返回默认的值
const user = {
info: {
name: "张三",
address: { home: "Shaanxi", company: "Xian" },
},
};
// obj是获取属性的对象,path是路径,fallback是默认值
function get(obj, path, fallback) {
const parts = path.split(".");
const key = parts.shift();
if (typeof obj[key] !== "undefined") {
return parts.length > 0 ?
get(obj[key], parts.join("."), fallback) :
obj[key];
}
// 如果没有找到key返回fallback
return fallback;
}
console.log(get(user, "info.name")); // 张三
console.log(get(user, "info.address.home")); // Shaanxi
console.log(get(user, "info.address.company")); // Xian
console.log(get(user, "info.address.abc", "fallback")); // fallback
另外一种方法
function get(obj: any, path: string, fallback: any) {
let parts = path.split('.');
return parts.reduce((a, b) => a && a[b], obj) || fallback
}
但是由于reduce会有循环,即使已经undefined,方法还是会继续循环下去,不会及时停止,增加不必要的执行次数