前端開發(fā)中常見的數(shù)據(jù)結(jié)構(gòu)優(yōu)化問題與解決
在實(shí)際前端開發(fā)中,后端返回的數(shù)據(jù)結(jié)構(gòu)往往不能直接滿足前端展示或業(yè)務(wù)邏輯的需求,需要進(jìn)行各種優(yōu)化處理。以下是幾個(gè)常見的數(shù)據(jù)結(jié)構(gòu)優(yōu)化問題及解決方案:
1. 嵌套層級(jí)過深的數(shù)據(jù)扁平化
問題場景:后端返回的數(shù)據(jù)嵌套層級(jí)過深,前端需要頻繁使用類似 data.a.b.c[0].d 的訪問方式,容易導(dǎo)致代碼冗余和空指針錯(cuò)誤。
// 后端返回的數(shù)據(jù)結(jié)構(gòu)
const response = {
user: {
info: {
basic: {
name: '張三',
age: 28,
address: {
province: '北京',
city: '北京市',
district: '朝陽區(qū)'
}
},
contact: {
phone: '13800138000',
email: 'zhangsan@example.com'
}
}
}
};
// 優(yōu)化方案:扁平化處理
function flattenObject(obj, prefix = '', result = {}) {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const newKey = prefix ? `${prefix}.${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null) {
flattenObject(obj[key], newKey, result);
} else {
result[newKey] = obj[key];
}
}
}
return result;
}
const flattened = flattenObject(response.user.info);
console.log(flattened);
/*
{
"basic.name": "張三",
"basic.age": 28,
"basic.address.province": "北京",
"basic.address.city": "北京市",
"basic.address.district": "朝陽區(qū)",
"contact.phone": "13800138000",
"contact.email": "zhangsan@example.com"
}
*/
2. 數(shù)組轉(zhuǎn)換為鍵值映射
問題場景:后端返回的是數(shù)組,但前端需要根據(jù)ID快速查找對(duì)應(yīng)的項(xiàng)。
// 后端返回的數(shù)據(jù)結(jié)構(gòu)
const products = [
{ id: 1, name: 'iPhone', price: 5999 },
{ id: 2, name: 'MacBook', price: 9999 },
{ id: 3, name: 'iPad', price: 3299 }
];
// 優(yōu)化方案:轉(zhuǎn)換為鍵值映射
const productMap = products.reduce((map, product) => {
map[product.id] = product;
return map;
}, {});
console.log(productMap);
/*
{
"1": { id: 1, name: "iPhone", price: 5999 },
"2": { id: 2, name: "MacBook", price: 9999 },
"3": { id: 3, name: "iPad", price: 3299 }
}
*/
// 使用示例
console.log(productMap[2].name); // 輸出: MacBook
3. 分頁數(shù)據(jù)合并與緩存
問題場景:分頁加載數(shù)據(jù)時(shí),需要合并多頁數(shù)據(jù)并保持唯一性。
// 模擬分頁請(qǐng)求
let cachedData = [];
let cachedIds = new Set();
function mergePaginationData(newPageData) {
// 過濾掉已存在的數(shù)據(jù)
const newItems = newPageData.filter(item => !cachedIds.has(item.id));
// 更新緩存
cachedData = [...cachedData, ...newItems];
newItems.forEach(item => cachedIds.add(item.id));
return cachedData;
}
// 第一頁數(shù)據(jù)
const page1 = [
{ id: 1, title: '文章1' },
{ id: 2, title: '文章2' }
];
console.log(mergePaginationData(page1));
// 輸出: [{ id: 1, title: '文章1' }, { id: 2, title: '文章2' }]
// 第二頁數(shù)據(jù)(包含重復(fù)項(xiàng))
const page2 = [
{ id: 2, title: '文章2' },
{ id: 3, title: '文章3' }
];
console.log(mergePaginationData(page2));
// 輸出: [{ id: 1, title: '文章1' }, { id: 2, title: '文章2' }, { id: 3, title: '文章3' }]
4. 時(shí)間戳格式化與排序
問題場景:后端返回的時(shí)間是時(shí)間戳或ISO格式,需要格式化為可讀形式并排序。
// 后端返回的數(shù)據(jù)結(jié)構(gòu)
const orders = [
{ id: 1, createTime: 1617187200000, amount: 100 },
{ id: 2, createTime: 1617273600000, amount: 200 },
{ id: 3, createTime: 1617091200000, amount: 150 }
];
// 優(yōu)化方案:格式化時(shí)間并排序
function formatTime(timestamp) {
const date = new Date(timestamp);
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
}
const processedOrders = orders
.map(order => ({
...order,
createTimeFormatted: formatTime(order.createTime)
}))
.sort((a, b) => b.createTime - a.createTime);
console.log(processedOrders);
/*
[
{
id: 2,
createTime: 1617273600000,
amount: 200,
createTimeFormatted: "2021-04-02"
},
{
id: 1,
createTime: 1617187200000,
amount: 100,
createTimeFormatted: "2021-04-01"
},
{
id: 3,
createTime: 1617091200000,
amount: 150,
createTimeFormatted: "2021-03-31"
}
]
*/
5. 樹形結(jié)構(gòu)數(shù)據(jù)處理
問題場景:后端返回的扁平數(shù)據(jù)需要轉(zhuǎn)換為樹形結(jié)構(gòu)展示。
// 后端返回的扁平數(shù)據(jù)
const flatData = [
{ id: 1, name: '部門A', parentId: null },
{ id: 2, name: '部門B', parentId: null },
{ id: 3, name: '小組A1', parentId: 1 },
{ id: 4, name: '小組A2', parentId: 1 },
{ id: 5, name: '小組B1', parentId: 2 },
{ id: 6, name: '成員A1-1', parentId: 3 }
];
// 優(yōu)化方案:轉(zhuǎn)換為樹形結(jié)構(gòu)
function buildTree(data, parentId = null) {
return data
.filter(item => item.parentId === parentId)
.map(item => ({
...item,
children: buildTree(data, item.id)
}));
}
const treeData = buildTree(flatData);
console.log(JSON.stringify(treeData, null, 2));
/*
[
{
"id": 1,
"name": "部門A",
"parentId": null,
"children": [
{
"id": 3,
"name": "小組A1",
"parentId": 1,
"children": [
{
"id": 6,
"name": "成員A1-1",
"parentId": 3,
"children": []
}
]
},
{
"id": 4,
"name": "小組A2",
"parentId": 1,
"children": []
}
]
},
{
"id": 2,
"name": "部門B",
"parentId": null,
"children": [
{
"id": 5,
"name": "小組B1",
"parentId": 2,
"children": []
}
]
}
]
*/
6. 大數(shù)據(jù)量的虛擬滾動(dòng)處理
問題場景:后端返回大量數(shù)據(jù),直接渲染會(huì)導(dǎo)致頁面卡頓。
// 模擬10000條數(shù)據(jù)
const bigData = Array.from({ length: 10000 }, (_, i) => ({
id: i + 1,
name: `項(xiàng)目${i + 1}`,
value: Math.random() * 100
}));
// 優(yōu)化方案:虛擬滾動(dòng)只渲染可見區(qū)域數(shù)據(jù)
function getVisibleData(data, scrollTop, itemHeight, containerHeight) {
const startIdx = Math.floor(scrollTop / itemHeight);
const endIdx = Math.min(
startIdx + Math.ceil(containerHeight / itemHeight),
data.length
);
return {
visibleData: data.slice(startIdx, endIdx),
startIdx,
endIdx
};
}
// 示例使用
const { visibleData, startIdx, endIdx } = getVisibleData(
bigData,
1500, // 滾動(dòng)位置
50, // 每項(xiàng)高度
500 // 容器高度
);
console.log(`顯示 ${startIdx}-${endIdx} 項(xiàng)數(shù)據(jù)`, visibleData);
// 輸出: 顯示 30-40 項(xiàng)數(shù)據(jù) [...]
7. 枚舉值轉(zhuǎn)換
問題場景:后端返回的是數(shù)字或字符串枚舉值,需要轉(zhuǎn)換為可讀文本。
// 后端返回的數(shù)據(jù)結(jié)構(gòu)
const orders = [
{ id: 1, status: 1, paymentType: 'ALIPAY' },
{ id: 2, status: 2, paymentType: 'WECHAT' },
{ id: 3, status: 3, paymentType: 'UNIONPAY' }
];
// 優(yōu)化方案:枚舉值映射
const statusMap = {
1: '待支付',
2: '已支付',
3: '已取消',
4: '已完成'
};
const paymentTypeMap = {
ALIPAY: '支付寶',
WECHAT: '微信支付',
UNIONPAY: '銀聯(lián)支付'
};
const processedOrders = orders.map(order => ({
...order,
statusText: statusMap[order.status],
paymentTypeText: paymentTypeMap[order.paymentType]
}));
console.log(processedOrders);
/*
[
{ id: 1, status: 1, paymentType: "ALIPAY", statusText: "待支付", paymentTypeText: "支付寶" },
{ id: 2, status: 2, paymentType: "WECHAT", statusText: "已支付", paymentTypeText: "微信支付" },
{ id: 3, status: 3, paymentType: "UNIONPAY", statusText: "已取消", paymentTypeText: "銀聯(lián)支付" }
]
*/
這些是前端開發(fā)中常見的數(shù)據(jù)結(jié)構(gòu)優(yōu)化問題,實(shí)際項(xiàng)目中可能會(huì)遇到更復(fù)雜的情況,但核心思路都是將后端返回的數(shù)據(jù)轉(zhuǎn)換為更適合前端展示和使用的形式。
到此這篇關(guān)于前端開發(fā)中常見的數(shù)據(jù)結(jié)構(gòu)優(yōu)化問題與解決的文章就介紹到這了,更多相關(guān)前端數(shù)據(jù)結(jié)構(gòu)優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談nodeName,nodeValue,nodeType,typeof 的區(qū)別
本文主要簡單介紹了nodeName,nodeValue,nodeType,typeof 的區(qū)別,算是知識(shí)點(diǎn)的一個(gè)小總結(jié),希望對(duì)小伙伴們有所幫助2015-01-01
JavaScript中的canvas?實(shí)現(xiàn)一個(gè)圓環(huán)漸變倒計(jì)時(shí)效果
這篇文章主要介紹了JavaScript中的canvas?實(shí)現(xiàn)一個(gè)圓環(huán)漸變倒計(jì)時(shí)效果,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
JS面向?qū)ο缶幊虒?shí)現(xiàn)的拖拽功能案例詳解
這篇文章主要介紹了JS面向?qū)ο缶幊虒?shí)現(xiàn)的拖拽功能,結(jié)合具體案例形式詳細(xì)對(duì)比分析了JS面向過程與面向?qū)ο髮?shí)現(xiàn)的拖拽功能相關(guān)操作技巧,需要的朋友可以參考下2020-03-03
JavaScript中輕松獲取頁面參數(shù)值的N種方法(含代碼示例)
本文旨在深入淺出地揭示如何在JavaScript中巧妙提取那些隱藏在URL背后的寶貴信息,從基礎(chǔ)方法到高級(jí)技巧,一網(wǎng)打盡,無論你是編程界的菜鳥還是久經(jīng)沙場的老將,這里都有值得你品鑒的“珍饈”,需要的朋友可以參考下2024-06-06
如何使用webpack5+TypeScript+npm發(fā)布組件庫
這篇文章主要介紹了如何使用webpack5+TypeScript+npm發(fā)布組件庫,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-04-04
js調(diào)用打印機(jī)打印網(wǎng)頁字體總是縮小一號(hào)的解決方法
直接調(diào)用window.print(),但是打印出來后,字體總是縮小一號(hào),后來直接target="_blank",就可以正常打印了,下面是實(shí)現(xiàn)代碼2014-01-01

