前端開發進階:前端開發中如何高效渲染大數據量?

来源:https://www.cnblogs.com/DTinsight/archive/2023/08/23/17651637.html
-Advertisement-
Play Games

在日常工作中,有時會遇到一次性往頁面中插入大量數據的場景,在數棧的[離線開發](https://www.dtstack.com/dtinsight/batchworks?src=szsm)(以下簡稱離線)產品中,就有類似的場景。本文將通過分享一個實際場景中的[前端開發](https://www.dt ...


在日常工作中,有時會遇到一次性往頁面中插入大量數據的場景,在數棧的離線開發(以下簡稱離線)產品中,就有類似的場景。本文將通過分享一個實際場景中的前端開發思路,介紹當遇到大量數據時,如何實現高效的數據渲染,以達到提升頁面性能和用戶體驗的目的。

渲染大數據量時遇到的問題

在離線的數據開發模塊,用戶可以在 SQL 編輯器中編寫 SQL,再通過整段運行/分段運行來執行 SQL。在點擊整段運行後,從運行成功日誌列印後到展示結果的過程中,有一段時間頁面會很卡頓,主要表現為編輯器編寫卡頓。

file

我們是在解決 SQL 最大運行行數問題時,發現了上述需要進行性能優化的場景。

先來梳理下當前代碼的設計邏輯:

file

· 前端將選中的 SQL 傳遞給服務端,服務端返回一個調度運行的 jobId

· 前端接著以該 jobId 輪詢服務端,查詢任務的執行狀態

· 當輪詢到任務已完成時,選中的 SQL 中如果有查詢語句,服務端則會按 select 語句的順序返回一個 sqlId 的數組集合

· 前端基於n個 sqlId 的集合,併發 n個 selectData 的請求

· 所有的 selectData 請求完成後渲染數據

為了保證結果最終的展示順序和 select 語句順序一致,我們為單純的 sqlIdList 迴圈方法加上了 Promise.allsettled 的方法,使得n個 selectData 的請求順序和 select 語句順序一致。

file

由上述邏輯可以看出,問題可能出現在如果選中的 SQL 中有大量 select 語句的話,會在「整段運行」完成後大批量請求 selectData 介面,再等待所有 selectData 請求完成後,集中進行渲染。此時,就會出現一次性往頁面中插入大量數據的場景,導致卡頓。那麼,我們怎麼解決上述問題呢?

解決思路

可以看出,上述邏輯主要有兩個問題:大批量請求 selectData 介面和集中性數據渲染。我們通過如下所示的解決思路去處理這些問題。

任務分組

依舊通過 Promise.allsettled 拿到所有 selectData 介面返回的結果,將原先集中渲染看作是一個大任務,我們將任務拆分成單個的 selectData 結果渲染任務。再根據實際情況,對單個任務進行分組,比如兩個一組,渲染完一組再渲染下一組。

拆分完任務,就涉及到了任務的優先順序問題,優先順序決定了哪個任務先執行。這裡採用最原始的“搶占式輪轉”,按 sqlIdList 的順序保留編輯器中的 SQL 順序。

Promise.allSettled(promiseList).then((results = []) => {
    const renderOnce = 2; // 每組渲染的結果 tab 數量
    const loop = (idx) => {
        if (promiseList.length <= idx) return;
        results.slice(idx, idx + renderOnce).forEach((item, idx) => {
            if (item.status === 'fulfilled') {
                handleResultData(item?.value || {}, sqlIdList[idx]?.sqlId);
            } else {
                console.error(
                    'selectExecResultDataList Promise.allSettled rejected',
                    item.reason
                );
            }
        });
        setTimeout(() => {
            loop(idx + renderOnce);
        }, 100);
    };
    loop(0);
});

請求分組 + 任務分組

問題中的大批量請求 selectData 介面,也是一個突破點。我們可以將請求進行分組,每次以固定數量的 sqlId 去請求 selectData 介面,比如每組請求 6 個 sqlId 的結果,當前組的請求全部結束後再進行渲染。為了保證效果最優,這裡也引入任務分組的思路。

const requestOnce = 6; // 每組請求的數量
// 將一維數組轉換成二維數組
const sqlIdList2D = convertTo2DArray(sqlIdList, requestOnce);
const idx2D = 0; // sqlIdList2D 的索引

const requestLoop = (index) => {
    if (!sqlIdList2D[index]) return;
    const promiseList = sqlIdList2D[index].map((item) =>
        selectExecResultData(item?.sqlId)
                                              );
    Promise.allSettled(promiseList)
        .then((results = []) => {
            const renderOnce = 2; // 每組渲染的結果 tab 數量

            const loop = (idx) => {
                if (promiseList.length <= idx) return;
                results.slice(idx, idx + renderOnce).forEach((item, idx) => {
                    if (item.status === 'fulfilled') {
                        handleResultData(item?.value || {}, sqlIdList[idx]?.sqlId);
                    } else {
                        console.error(
                            'selectExecResultDataList Promise.allSettled rejected',
                            item.reason
                        );
                    }
                });
                setTimeout(() => {
                    loop(idx + renderOnce);
                }, 100);
            };
            loop(0);
        })
        .finally(() => {
            requestLoop(index + 1);
        });
};
requestLoop(idx2D);

請求分組

上一種方案的代碼相對來說又些難以理解,屬於上午寫,下午忘的邏輯,註釋也不好寫,不利於維護。基於實際情況,我們嘗試下僅對請求作分組處理,看看效果。

const requestOnce = 3; // 每組請求的數量
// 將一維數組轉換成二維數組
const sqlIdList2D = convertTo2DArray(sqlIdList, requestOnce);
const idx2D = 0; // sqlIdList2D 的索引

const requestLoop = (index) => {
    if (!sqlIdList2D[index]) return;
    const promiseList = sqlIdList2D[index].map((item) =>
        selectExecResultData(item?.sqlId)
                                              );
    Promise.allSettled(promiseList)
        .then((results = []) => {
            results.forEach((item, idx) => {
                if (item.status === 'fulfilled') {
                    handleResultData(item?.value || {}, sqlIdList[idx]?.sqlId);
                } else {
                    console.error(
                        'selectExecResultDataList Promise.allSettled rejected',
                        item.reason
                    );
                }
            });
        })
        .finally(() => {
            requestLoop(index + 1);
        });
};
requestLoop(idx2D);

file

解決思路解析

· 解決大數據量渲染的問題,常見方法有:時間分片虛擬列表

· 解決同步阻塞的問題,常見方法有:任務分解、非同步等

· 如果某個任務執行時間較長的話,從優化的角度,我們通常會考慮將該任務分解成一系列的子任務

在任務分組一節,我們將 setTimeout 的時間間隔設置為 100ms,也就是我認為最快在 100ms 內能完成渲染。但假設不到 100ms 就完成了渲染,那麼就需要白白等待一段時間,這是沒有必要的,這時可以考慮 window.requestAnimationFrame 方法。

- setTimeout(() => {
+ window.requestAnimationFrame(() => {
      loop(idx + renderOnce);
- }, 100);
+ });

第三節的請求分組,實際上已經達到了渲染任務分組的效果。本文更多的是提供一個解決思路,上述方式也是基於對時間分片的理解實踐。

在軟體開發中,性能優化是一個重要的方面,但並不是唯一追求,往往還需要考慮多個因素,包括功能需求、可維護性、安全性等等。根據具體情況,綜合使用多種技術和策略,找到最佳的解決方案,才是最終目的。

《數棧產品白皮書》:https://www.dtstack.com/resources/1004?src=szsm

《數據治理行業實踐白皮書》下載地址:https://www.dtstack.com/resources/1001?src=szsm

想瞭解或咨詢更多有關袋鼠雲大數據產品、行業解決方案、客戶案例的朋友,瀏覽袋鼠雲官網:https://www.dtstack.com/?src=szbky

同時,歡迎對大數據開源項目有興趣的同學加入「袋鼠雲開源框架釘釘技術qun」,交流最新開源技術信息,qun號碼:30537511,項目地址:https://github.com/DTStack


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ## 一、為什麼要使用 LINQ 要理解為什麼使用 LINQ,先來看下下麵的例子 例子:要統計字元串中每個字母出現的頻率(忽略大小寫),然後按照從高到低的順序輸出出現頻率高於2次和其出現的的頻率。如果用傳統的 Sql 語句來寫,一定是非常的繁雜,如果用 LINQ 語句來寫,效果如下 ```c# st ...
  • [toc] # Linux運維工程師面試題(1) > 祝各位小伙伴們早日找到自己心儀的工作。 > 持續學習才不會被淘汰。 > 地球不爆炸,我們不放假。 > 機會總是留給有有準備的人的。 > 加油,打工人! ## 1 別名、內部命令、外部命令的執行順序 命令執行尋找順序:別名 > 內部命令 > 外部命 ...
  • [toc] ### 1.文件操作 #### 1.1 創建文件 ``` shell # touch+文件名 # 例子: # 創建一個文件 touch hello.c # 創建多個文件 touch hello.c hi.c ``` #### 1.2 刪除文件 ``` shell # rm+文件名 # 例 ...
  • 因為我用的是windows伺服器,因此需要一臺虛擬機,用來安裝centos,虛擬機的安裝網上好多教程,這裡不做過多介紹 這次同樣是按步操作 在本地伺服器創建下載目錄 -> 將yum文件下載到本地 -> 在遠程伺服器上創建目錄 -> 上傳文件到遠程伺服器目錄 -> 使用命令安裝yum到伺服器上 這次的 ...
  • # 任務與協程 ## 區別 > 一個程式可以只有任務、只有協程、二者都有,但不可以通過隊列/信號量互相傳遞數據 ## 任務特點 1. 任務之間可以互相獨立 2. 每個任務分配自己的堆棧,提高了RAM使用率 3. 操作簡單、按優先順序搶占式執行 4. 搶占容易導致重入(執行任務時被其他線程或進程調用了) ...
  • ![](https://img2023.cnblogs.com/blog/3076680/202308/3076680-20230822115524099-438612716.png) # 1. 在執行語句之前,會先檢查下列事項 ## 1.1. 是否有許可權執行該語句 ## 1.2. 是否有許可權訪問指 ...
  • 排序查詢:select 欄位列表 from [表名] order by [欄位名1] [asc升序/desc降序,預設值為升序],[欄位名2] [排序方式];//欄位名1為優先順序排序,如果欄位名1有相同的,再以欄位名2排序 聚合函數: count 統計數量(一般不選null的列) max 最大值 m ...
  • ## 1、字元集概述 - Oracle語言環境的描述包括三部分:language、territory、characterset(語言、地域、字元集) - language:主要指定伺服器消息的語言,提示信息顯示中文還是英文 - territory:主要指定伺服器的數字和日期的格式 - charact ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...