小冊
這是我整理的學習資料,非常系統和完善,歡迎一起學習
背景
最近海外應用有某些使用者反饋,開啟頁面比較卡頓,後來針對這個問題做了層最佳化
問題
這裡我們用微信好友列表
為例子,因為列表功能比較常見,詳細分析下常見專案存在的一些問題,以及如何最佳化
通常我們的專案中都是有列表
這種場景,然後點選列表裡面的具體item
,就去到具體的詳情頁
我們可能是這麼處理
<List>
{
list.map((item)=><ListItem id={Item.useId}/>)
}
</List>
然後我們點選好友列表進入具體的詳情頁根據useId
再去拿具體的資訊
getUserInfoById(id)
預載入
但是這裡就會存在一個,進入詳情頁
的時候,開啟會慢,所以這裡一般會先做資料預載入
,也就是在好友列表的時候
我就想拿到這個詳情頁資料
,這時候我們可能這麼處理
一次性返回資料
<List>
{
list.map((item)=><ListItem detail={Item.detail}/>)
}
</List>
後端支援在好友列表的時候同時返回具體的detail
資訊,這樣就不用去走一次getUserInfoById(id)
,但是這裡也會存在一個問題,好友列表這個介面太冗餘,而且資料量太大,開啟頁面的時候也會出現載入慢的場景
,所以這個策略也只能針對資料量較小的情況採取
預載入getUserInfoById介面
那麼幹脆一點,我們請求完好友介面後,再根據使用者Id,在App下偷偷請求getUserInfoById
介面
getUserInfoById(1)
getUserInfoById(2)
getUserInfoById(3)
...
````
這樣就會出現一個問題,後端服務可能扛不住我們這樣頻繁的請求,所以有什麼辦法解決呢?那就是`請求合併`,`將多個重複請求(引數不一樣),合併成一個,也就是將引數合併`
## 請求合併
const fetchUserInfoBatched = createBatchedRequest<string, UserBaseInfo>(
async (userIds) => {
const { data } = await request.post('/api/user/list', {
userIds,
});
return data;
},
500 // 設定延遲時間為500毫秒
);
// 使用示例
async function getUserInfo() {
const user1 = await fetchUserInfoBatched(1);
const user2 = await fetchUserInfoBatched(2);
const user3 = await fetchUserInfoBatched(3);
console.log(user1, user2, user3);
}
getUserInfo();
### createBatchedRequest
interface BatchRequestItem<T, R> {
params: T;
resolve: (r: R) => void;
reject: (reason: unknown) => void;
}
/**
- 建立批次請求的函式
- 在一定延遲時間內的所有請求都會被合併提交併批次傳送
- @param batchFunction 合併後的請求函式
- @param delay 延遲時間,以毫秒為單位
*/
export function createBatchedRequest<T, R>(
batchFunction: (batchParams: T[]) => Promise<R[]>,
delay = 200
): (params: T) => Promise<R> {
const batchQueue: BatchRequestItem<T, R>[] = [];
let isBatching = false;
let timer: NodeJS.Timeout | null = null;
async function executeBatchedRequest() {
if (isBatching) return;
isBatching = true;
const itemsToBatch = [...batchQueue];
batchQueue.length = 0;
try {
const batchedResult = await batchFunction(itemsToBatch.map((item) => item.params));
itemsToBatch.forEach((item, index) => {
item.resolve(batchedResult[index]);
});
} catch (error) {
itemsToBatch.forEach((item) => {
item.reject(error);
});
} finally {
isBatching = false;
}
}
return (params: T): Promise<R> => {
return new Promise<R>((resolve, reject) => {
batchQueue.push({
params,
resolve,
reject,
});
// Execute the batched request after the specified delay
if (!timer) {
timer = setTimeout(() => {
executeBatchedRequest();
timer = null;
}, delay);
}
});
};
}
* **批次請求管理**: `createBatchedRequest` 函式用於管理批次請求,它可以將多個獨立的請求合併成一個批次請求,以減少不必要的網路請求次數。
* **引數說明**:
* `batchFunction` 引數是一個函式,接受一個陣列 `batchParams` 作為引數,返回一個 Promise,用於處理合併後的請求並返回結果。
* `delay` 參數列示延遲時間,以毫秒為單位。在指定的延遲時間內,所有的請求會被收集起來,然後一次性傳送給 `batchFunction` 處理。
* **請求佇列**: 函式內部維護一個請求佇列 `batchQueue`,用於儲存待合併的請求項。每個請求項包含了請求的引數、成功回撥函式 `resolve` 和失敗回撥函式 `reject`。
* **執行批次請求**:
* 當有請求呼叫返回的函式時,它會將請求引數和相應的回撥函式新增到請求佇列 `batchQueue` 中。
* 使用定時器控制,在指定的延遲時間後,會執行 `executeBatchedRequest` 函式。
* `executeBatchedRequest` 函式會檢查是否已經有批次請求正在處理(`isBatching` 標誌),如果有,則不進行處理,直到當前批次請求完成。
* 如果沒有正在處理的批次請求,它會取出請求佇列中的所有請求項,合併引數後呼叫 `batchFunction` 處理請求。
* 成功或失敗後,會分別呼叫請求項中的 `resolve` 或 `reject` 回撥函式,將結果返回給每個獨立的請求。
## 面試
> 最近整理了一套面試小冊,有`線上版和離線版本`
離線版本效果如下,可新增微信`linwu-hi`獲取,閱讀效果非常不錯
![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/886db51df5a64001bbaf66c2dcf7884a~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=480\&h=480\&e=png\&b=faf7f7)
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ac45d166a77942df95bbb0552d1b8208~tplv-k3u1fbpfcp-jj-mark:1200:0:0:0:q75.avis)