(1)数据、接口整合思维,能合并成一个数组或者接口的尽量合并,代码尽量使用高级一点的写法,不要让代码冗余;
(2)后端返回的数据结构是一维数组,类似于
let resData=[
{a:'11',b:'11',level:'0'},
{a:'22',b:'22',level:'1'},
{a:'33',b:'33',level:'0'},
{a:'44',b:'44',level:'1'},
{a:'55',b:'55',level:'1'},
]
现在想根据每个对象中的level为0时变成一个二维数组,每个level=0的数组包含后面的level=1的对象,直至遇到下一个level为0的对象,重新生成一个二维数组,并且可以反转数组,且不会更改每个二维数组内部的顺序
if (resData) {
let j = -1;
let tempData = resData.reduce((pre, cur) => {
if (['0', 0].includes(cur?.level)) {
j++;
pre.push([]);
}
j > -1 && pre[j].push(cur);
return pre;
}, []);
/* 将tempData反转后并按顺序展开成一维数组 */
let reverseData=tempData.reverse().flat()
console.log(tempData,reverseData);
}
(3)对象里面过滤全部符合条件的对象
const obj = {
a: 21,
b: 22,
c: 23
}
Let aa=Object.fromEntries(Object.entries(obj).filter(([_,i])=>i>21))
console.log(obj,aa);
(4)正则与replace连用
let val='11'
let myval='112233441121122211144'
let reg=new RegExp(val,'g')
let vals=myval.replace(reg,'')
(5)If与switch连用,提高代码可读性
if(){
switch (key) {
case value:
break;
default:
break;
}
}else{}
(6)判定arr.length时,如果一直报错length,那首先要判断arr是否存在,如:if(arr&&!arr.length)或者使用可选链if(arr?.length)
(7)if条件语句可以直接写成
判定条件&&(执行语句);(执行语句)
(8)??(空值合并运算符),es10新增语法,与||类似,都是判断符号左边为false时,直接取符号右边的值,但是??与||的区别在于0值和’ ’(空字符串)判定为true
let a=0,
b=a??'11'
console.log(b);
b=0
(9)禁止a标签的href事件,<a href=’javascript:;’ @click=’deal’>
(10)模板字符串里面不能用this的写法
(11)if用return 或者return early打断,for循环用break打断
(12)处理bug流程?
1、问题复现,定位问题
2、排查出问题点,首先确定是否是语法错误,不是的话找出错误代码位置(通过控制台报错信息、console.log、debugger等手段找出具体问题点,debugger可以执行当前不好执行的部分具体代码,快速了解当前代码是否有错误)
3、将debugger放在该测试的地方,如果在console.log里面sources里面直接执行的话,看看会不会出现弹框,出现弹框说明执行了代码,且点击↘可以直接进行下一行代码运行,鼠标移入this.value可以查看当前的值
4、Vue-develop使用,在测试环境中找到子组件对应的模块,可以看到子组件的状态,如父组件传递给子组件的值有没有传过去等
5、修正问题
(13)有表单验证错误信息展示的需求,但是错误信息默认不展示,当出现错误时展示,可以在表单的下方设置一个空的div,让div先占位着,当错误时显示div的信息,这是一种编程思维,经常能用的到
(14)Foreach里面不能使用splice删除数组中的某一项,因为index会受影响,要使用fliter来进行过滤、选出不符合的项
arr = arr.filter(item => {
return item.num != 0
})
(15)将一个内嵌数组转化成一维数组
const deep= function(arr) {
return [].concat(...arr.map(v=>Array.isArray(v)?deep(v):v))
}
let res=deep([1,[2,3,[7]],[[1],[2,8]]])
或者直接用lodash中的_.flattenDeep()
(16)获取一个1-100的数组
Lodash方法
let arr=_.range(1,101)
手写
let arr=Array(100).fill(1).map((o,v)=>v+1)
(17)将数组的两项数据调换位置
[cloneColumn[5], cloneColumn[0]] = [cloneColumn[0], cloneColumn[5]];
(18)将数组中对象的键名换成自己想要的键名
export const filterArrayProps=(array)=>{
let newarr=array.map((item) => {
return Object.assign({},{'label':item.name,'value':item.id})
})
return newarr
}
(19)在后端返回的数组中,每个对象中增添一个序号排序字段或者其他字段
export const filterArrayNumSort=(array,pageNum,pageSize)=>{
let _arr=[]
array.map((item,index) => {
_arr.push(Object.assign({},item,{'mysort':(pageNum-1)*pageSize+index+1}))
})
return _arr
}
(19.1)将一个数组1中的对象2中的数组3的对象4提取出来放到一维结构中,也就是放到数组1中
let datas = [
{
devzName: "上海浦东新区",
DEVZCode: "Gasnjkd",
hits: [
{ IndicName: "GDP", Mvalue: "15456" },
{ IndicName: "GDP增速", Mvalue: "5%" },
],
},
{
devzName: "北京大兴经济开发区",
DEVZCode: "Kjhashd",
hits: [
{ IndicName: "GDP", Mvalue: "74564" },
{ IndicName: "GDP增速", Mvalue: "6%" },
],
},
{
devzName: "合肥经济技术开发区",
DEVZCode: "Hhskjhd",
hits: [
{ IndicName: "GDP", Mvalue: "567897" },
{ IndicName: "GDP增速", Mvalue: "8%" },
],
},
];
//第一种写法,双层map
const formatData1 = (data) => {
let mergeData = data.map((o, i) => {
let middleHit = o.hits.map((k, l) => {
let source = {};
source[k.IndicName] = k.Mvalue;
source[k.IndicName + "_guid"] = k.Mvalue + "111";
// middleHit.push({ ...source });
return source //此处就是直接返回二级数组中的hits中的操作后的每一个对象直接返回了,未合并在一起
});
return ({ ...o, ...middleHit });
});
return mergeData;
};
console.log(formatData1(datas));
// [
// {
// {GDP: '15456', GDP_guid: '15456111'},
// {GDP增速: '5%', GDP增速_guid: '5%111'},
// DEVZCode: "Gasnjkd",
// devzName: "上海浦东新区",
// }
// ]
const formatData2 = (data) => {
let mergeData = data.map((o, i) => {
let middleHit = o.hits.reduce((pre, cur) => {
let source = {};
source[cur.IndicName] = cur.Mvalue;
source[cur.IndicName + "_guid"] = cur.Mvalue + "111";
return { ...pre, ...source }; //reduce思想很重要,它把二级数组中的hits中的对象操作后合并成一个对象了,不是分开单独返回的对象
}, {});
return { ...o, ...middleHit };
});
return mergeData;
};
console.log(formatData2(datas));
// [
// {
// DEVZCode: "Gasnjkd"
// GDP: "15456"
// GDP_guid: "15456111"
// GDP增速: "5%"
// GDP增速_guid: "5%111"
// devzName: "上海浦东新区"
// hits: (2) [{…}, {…}]
// },
// {
// DEVZCode: "Kjhashd"
// GDP: "74564"
// GDP_guid: "74564111"
// GDP增速: "6%"
// GDP增速_guid: "6%111"
// devzName: "北京大兴经济开发区"
// hits: (2) [{…}, {…}]
// }
// ]
(20)将一个大数组分割成很多个小数组?
groupslice(array,subGroupLength){
let index = 0;
let newArray = [];
while(index < array.length) {
newArray.push(array.slice(index, index += subGroupLength));
}
return newArray;
或者使用lodash的_.chunk方法
},
(21)将已知数据转化成与其相对应的其他数据
const actions=new Map(
[//只能为数组
[1,['success','fail']],
[2,['success2','fail2']],
[3,['success3','fail3']],
['default',['success4','fail4']],
]
)
Console.log(actoins.get(1))
(22)循环获取数组中的值
const a = [1,2,3,4,5]
for(let i=0;i< 100;i++){
console.log(a[i % (a.length)]);
}
(23)console.log/error/warn/table(将数组内对象结构以表格形式打印出来)/count(记录执行次数)/time、timeEnd(前后包围执行程序,计算执行时间)
(24)查找离今天最近的数据并将此数据的下标返回
const findClosestData = useMemoizedFn((data: any[]) => {
let addIdxArray: any[] = [];
data.map((item, index) => addIdxArray.push(Object.assign({}, item, { mysort: index })));
let [closest] = addIdxArray.sort((a, b) => {
//@ts-ignore
const [aDate, bDate] = [a, b].map((d) => Math.abs(new Date(d?.happenDate) - new Date()));
return aDate - bDate;
});
return closest?.mysort;
});
(25)数据归类算法
//分类算法
let arr=[
{value:'1111',key:'a'},
{value:'2222',key:'b'},
{value:'3333',key:'c'},
{value:'4444',key:'a'},
{value:'5555',key:'a'},
]
//1、forEach模式
// let obj={}
// arr.forEach(o=>{
// if(obj.hasOwnProperty(o.key)){
// obj[o.key].push(o.value)
// }else{
// obj[o.key]=[o.value]
// }
// })
//2、reduce模式
// const obj=arr.reduce((pre,cur)=>{
// if(pre.hasOwnProperty(cur.key)){
// pre[cur.key].push(cur.value)
// }else{
// pre[cur.key]=[cur.value]
// }
// return pre
// },{})
//3、先定义集合,再填充数据
// const draft={
// a: '',
// b: '',
// c: '',
// };
// arr.forEach(o=>{
// draft[o.key] = (draft[o.key] ?? '') + `${o.value},`;
// })
1、2是可以不需要知道初始的key值的,但是3需要知道初始的对象key值
(25)递归数据
function turnTreeIntoList (array) {
return array.map(item => [].concat(item, ...item.items)
)
}
const treeData = [{
key: '全部',
title: '全部',
isLeaf: false,
children: [{
key: '数据库',
title: '数据库',
isLeaf: false,
children: [
{
key: 'mysql',
title: 'mysql',
isLeaf: false,
children: [
{
key: '137',
title: 'QPS',
isLeaf: true,
}, {
key: '146',
title: 'MySQL进程信息',
isLeaf: true,
},
],
},
{
key: 'oracle',
title: 'oracle',
isLeaf: false,
children: [
{
key: '137',
title: 'QPS',
isLeaf: true,
},
],
},
],
}],
}];
// 获取树的所有祖先节点名称
const getTreeParents = (data) => {
const parentKey = [];
data.forEach((item) => {
/**`在没有限制的情况下,递归运算会无终止地自身调用,所以 在递归运算中要结合 条件语句(if)进行控制`,
`只有在某个条件成立时才允许执行递归,否则不允许调用自身`*/
if (item.children && item.children.length) {
parentKey.push(item.key);
const temp = getTreeParents(item.children);//此处得到的就是children跑完getTreeParents函数得到的parentKey结果["mysql", "oracle"]
if (temp.length) {
parentKey.push(...temp);
}
}
});
return parentKey;
};
const parentKeys = getTreeParents(treeData);
console.log(parentKeys); // ["全部", "数据库", "mysql", "oracle"]
/** 递归初始接口数据,添加level、title、key、checkable字段 */
const formatTreeData = (array: any[], levelName = 'level', childrenName = 'children') => {
if (!Array.isArray(array)) return [];
const recurite = (arr: any[], parentsKeyList: string[], childrenKeyList?: string[], level = 0) => {
// debugger;
level++;
return arr.map((item: Record<string, any>) => {
item[levelName] = level;
item['title'] = (
<div title={item.blockSName} style={{ width: MaxWidthType.get(item.level) }}>
{item.blockSName}
</div>
);
item['key'] = item.blockCode;
/** 添加当前节点的所有父级节点集合 */
item['parentsKeyLists'] = [item.parentBlockCode, ...parentsKeyList];
const child = item[childrenName];
/** 给一级树数据添加checkable=false,根据此属性去除一级的复选框*/
if (item?.level === 1) {
item['checkable'] = false;
item.childrenKeyList = [];
}
if (childrenKeyList) childrenKeyList.push(item.key);
if (child && child.length)
recurite(
child,
item['parentsKeyLists'],
//在一级节点上添加当前所有子节点的key,用来对比checkedKeys,从而实现当板块树下方选中后,一级节点高亮
item?.level === 1 ? item['childrenKeyList'] : childrenKeyList,
level,
);
return item;
});
};
let finalFormatTreeData = recurite(array, []);
finalFormatTreeData.forEach((item) => {
let intersectionData = intersection(item.childrenKeyList, checkedKeys);
item['title'] = (
<div
title={item.blockSName}
style={{ width: MaxWidthType.get(item.level), color: intersectionData?.length ? '#0171F6' : '#141414' }}
>
{item.blockSName}
</div>
);
});
return finalFormatTreeData;
};
搜索一组树数据的节点,返回当前子节点和其父级及祖先节点
const findMenuItem = useMemoizedFn((keyword: string) => {
const judgeFn = (node: any) => {
return node.title?.indexOf(keyword) !== -1;
};
function _filter(nodes: DataNodeWithLevel[] | undefined, judge: Function) {
if (!nodes || !nodes.length) return [];
const child = [];
for (let i = 0, l = nodes.length; i < l; i++) {
const node = nodes[i];
// 自己本身符合条件(如果有匹配的则不用递归了,直接取下面所有的子节点)
if (judge(node)) {
child.push(node);
continue;
}
const sub = _filter(node?.children || [], judge);
// 以下两个条件任何一个成立,当前节点都应该加入到新子节点集中
// 1. 子孙节点中存在符合条件的,即 sub 数组中有值
// 2. 自己本身符合条件
if ((sub && sub.length) || judge(node)) {
if (node.children) node.children = sub;
child.push(node);
}
}
return child;
}
return _filter(cloneDeep(withLevelData), judgeFn);
});
(26)前端模拟并发请求
/**
* 异步任务的并发控制函数
* @param poolLimit 异步任务的并发限制数
* @param array 异步任务队列
* @param iteratorFn 迭代函数,用于实现对每个任务项进行处理
* @returns
*/
export async function asyncPool(poolLimit: number, array: any, iteratorFn: (item: any) => Promise<any>) {
const ret = []; // 存储所有的异步任务
const executing: any[] = []; // 存储正在执行的异步任务
for (const item of array) {
/* 调用iteratorFn函数创建异步任务 */
const p = iteratorFn(item);
ret.push(p); // 保存新的异步任务
/* 当poolLimit值小于或等于总任务个数时,进行并发控制 */
if (poolLimit <= array.length) {
/* 当任务完成后,从正在执行的任务数组中移除已完成的任务 */
const e: any = p.finally(() => executing.splice(executing.indexOf(e), 1));
executing.push(e); // 保存正在执行的异步任务
if (executing.length >= poolLimit) {
try {
await Promise.race(executing); // 等待较快的任务执行完成
} catch (error) {
if (process.env.NODE_ENV === 'development') {
console.error(error);
}
}
}
}
}
return Promise.all(ret); // 最后剩的用 all 跑完
}
//调用
const fetchData = useMemoizedFn(async (lists) => {
const handleRequest = (item: any) => {
const { beginTime, endTime, calcBase } = topFilterResult;
const params = {
...item.content,
beginTime,
endTime,
calcBase,
bondInterestName: item.planId,
};
for (const key in params) {
if (Object.prototype.hasOwnProperty.call(params, key)) {
const element = params[key];
if (isArray(element)) params[key] = element.join(',');
}
}
return request
.post(`/finchinaAPP/v1/finchina-bond/v1/bond/interest/list`, {
// .post(`/test/v1/bond/interest/list`, {
data: JSON.stringify(params),
headers: { 'Content-Type': 'application/json; charset=UTF-8' },
})
.then((res: any) => {
if (res?.data) {
const { bondInterestName, bondInterestValue } = res.data;
dispatch((draft) => {
draft.curveInvest[bondInterestName] = bondInterestValue;
});
}
});
};
console.time('fetchTime');
await asyncPool(6, lists, handleRequest);
console.timeEnd('fetchTime');
});