我所嘗試過的每一個JSON檢視器都做相同的事:它美化輸出。增加縮排、著色鍵、摺疊括號。對於10行的配置有用 — 對於開發者每天處理的實際數據結構則沒用,那是一個物件的陣列
。一個美化輸出的50行API響應仍然是你上下滾動500行的內容。你不能說__JHSNS_SEG_e984f6ba_4__"只顯示管理員" 不需要重新讀取每一條記錄。您無法按日期排序。您無法跨行搜尋.
這不是查看器問題 — 這是格式錯誤。物件的陣列想要成為 表格 :
┌────┬───────┬───────┬────────┐
│ ID │ NAME │ ROLE │ ACTIVE │
├────┼───────┼───────┼────────┤
│ 1 │ Alice │ admin │ ● Yes │
│ 2 │ Bob │ user │ ● Yes │
│ 3 │ Carol │ user │ ● No │
│ 4 │ Dave │ admin │ ● Yes │
└────┴───────┴───────┴────────┘
可排序的欄位。可搜尋的列。點擊任何列以展開完整的嵌套詳情。相同數據 — 但您掃描 它而不是 讀取 它.
所以我建立了 prettyjsonxml.com — 一個 JSON 和 XML 查看器,將陣列轉換為真正的表格(還有可摺疊的樹狀視圖、格式化、最小化、base64 圖片預覽)。一個 HTML 檔案。沒有後端。沒有建構步驟。完全在瀏覽器中運行;你的數據永遠不會離開你的電腦。
我沒有預期的:讓表格視圖在 9 MB API 响應上實際變得快速,比看起來的難得多。這裡是未經編輯的故事.
天真的版本:有效,然後無效
V1 是直接的:
const data = JSON.parse(text);
renderTable(data);
function renderTable(items) {
const tbody = document.querySelector('tbody');
items.forEach(row => {
const tr = document.createElement('tr');
// ... build cells
tbody.appendChild(tr);
});
}
對我測試的 5 行示例看起來很漂亮。然後我粘貼了一個真正的 API 响應。瀏覽器凍結了1.5秒.
凍結有兩個來源.
-
JSON.parse在9 MB的區塊上凍結主線程約500毫秒. - 創建30,000 × 2列(主 + 詳細)= 60,000個DOM節點凍結約1500毫秒.
你無法讓它們變得快,但你可以讓它們不凍結UI.
第一階段:content-visibility: auto — 單行獲勝
如果告訴瀏覽器可以,現代瀏覽器會跳過對屏幕外內容的佈局。一條CSS規則:
.data-table tr.row-main {
content-visibility: auto;
contain-intrinsic-size: auto 40px;
}
content-visibility: auto 告訴瀏覽器:"在這個元素即將滑入視野之前,不必煩惱計算它的佈局。" contain-intrinsic-size 提供了一個暫存的身高,以便捲動條仍然代表整個文件.
總渲染時間沒有改變 — 工作仍然進行 — 但 感知效能提升了,因為瀏覽器可以優先繪製可見部分。非常少使用。適用於約95%的現代瀏覽器.
第二階段:使用 Web Workers 進行 JSON.parse — 這個反直覺的教訓
下一次凍結是JSON.parse本身。傳統智慧:用 Web Worker 將昂貴的解析作業移離主執行緒。
const worker = new Worker(URL.createObjectURL(new Blob([`
self.onmessage = (e) => {
const parsed = JSON.parse(e.data);
self.postMessage(parsed);
};
`], { type: 'application/javascript' })));
worker.postMessage(largeJsonString);
worker.onmessage = (e) => render(e.data);
完成。主執行緒保持反應靈活。對吧?
實際上感覺更慢了。
原因如下:當工作執行緒透過回傳解析過的物件時postMessage,主執行緒必須 結構化克隆 整個物件圖才能接收它。對於一個包含30,000個物件的陣列,這個克隆需要300–500毫秒 — 同樣也在主執行緒。
所以我就成功地將500毫秒的 JSON.parse 移到了主執行緒之外,並增加了400毫秒的結構化克隆。淨收益:約100毫秒。而使用者感知到的是卡頓稍後 在流程中,在他們點擊預期能立即得到結果的按鈕之後.
Web Workers 是用於 CPU 繫結工作,其結果不必回傳. 當 95% 的工作量是解析後的物件本身時,結構複製的成本會主導收益。
我保留了用於格式化/最小化的工人(輸出是字串,複製成本低廉)。對於解析然後渲染的流程,它幾乎沒有產生淨收益。真正的修復在其他地方.
第三階段:虛擬滾動 — 實際的修復
對於有30,000行的表格,你不需要渲染30,000行。你只需要渲染用戶能看到的約50行,並在用戶滾動時替換它們.
與 <table> 的陷阱在於:你不能position: absolute 列 (表格佈局不允許這樣)。改用 spacer 列:
<table>
<thead>...</thead>
<tbody>
<tr style="height:850px"></tr> <!-- spacer for rows above viewport -->
<tr>row 21</tr>
<tr>row 22</tr>
...
<tr>row 70</tr>
<tr style="height:1200px"></tr> <!-- spacer for rows below viewport -->
</tbody>
</table>
在每次滾動事件中,重新計算哪些列是可見的並替換它們:
function onScroll() {
const tbodyRect = tbody.getBoundingClientRect();
const viewTop = Math.max(0, -tbodyRect.top);
const viewBottom = viewTop + scrollContainer.clientHeight;
const startRow = Math.max(0, Math.floor(viewTop / ROW_HEIGHT) - 10);
const endRow = Math.min(items.length, Math.ceil(viewBottom / ROW_HEIGHT) + 10);
// Remove old visible rows, build new ones from items[startRow..endRow]
// Adjust spacer heights so scrollbar position stays correct
}
對於一個30,000行的JSON陣列,這從"完全無反應的tab"變為"順滑的60 fps滾動。"即使在更謙卑的尺寸下——比如幾百行——優勢在於搜尋和排序變得即時,因為它們現在運作在JavaScript陣列上,而不是通過遍歷千個DOM節點。
我花費20分鐘解決的bug:我的虛擬滾動器聆聽到window.scroll。但我的頁面有body { overflow: hidden }和<main> { overflow: auto } — 所以window.scroll 從未發生。實際的滾動事件來自<main>。
// Walk up to find the nearest ancestor that actually scrolls
function findScrollContainer(el) {
let p = el.parentElement;
while (p && p !== document.body) {
const oy = getComputedStyle(p).overflowY;
if ((oy === 'auto' || oy === 'scroll') && p.scrollHeight > p.clientHeight) return p;
p = p.parentElement;
}
return window;
}
在執行時總是解析滾動容器。不要假設它是window。
textarea 捕獲
一個我沒預期的凍結:將一個 9 MB 的字串指定給<textarea>.value它自己會阻擋主執行緒約300–500毫秒。即使大部分的文本內容在滾動視窗之外,瀏覽器也必須計算其佈局。
修復:為了檔案>5 MB,請將文本區域清空,並顯示一個帶有樣式的「載入狀態」面板。
if (file.size > 5 * 1024 * 1024) {
editor.value = '';
showLoadedOverlay(file.name, file.size);
} else {
editor.value = text;
}
數據仍然可以解析並呈現在檢視器中 — 只是沒有在可編輯的文本區域裡。對於9 MB的文件,沒有人還想手動編輯的。
我會做不同的改變
- 從虛擬滾動開始。 不要把它作為第三階段添加。它是唯一可以擴展的東西。其他一切都是修飾。
- 質疑「把它移到Worker」的直覺。 Worker 非常適合計算,你不需要回傳。不適合 parse-then-clone 的流程.
-
在適用的地方使用
content-visibility: auto. 基本上免費. - 萬一用實際的生產數據測試. 我的 5 行測試案例隱藏了所有有趣的 bug.
單文件的事情
我一直在跟自己爭論是否要加上一個編譯步驟。"直接打包就好了,直接拆分成模組,直接加 TypeScript…" 那個原型的技術上更簡潔,但實質上更糟糕 — 現在我必須托管更多文件,擔心緩存破壞,維護一套工具鏈.
對於一個主張是"100% 在你的瀏覽器中,沒有伺服器"的工具,把它作為一個你可以Save As 和離線運行是正確的產品決策。實用性優先於純粹性。
最終:~225 KB 單個 HTML,無依賴,無建構,直接從 Cloudflare Pages 提供服務。
如果你想要嘗試它:prettyjsonxml.com — 貼上任何 JSON 或 XML,以可排序表格或可摺疊樹狀視圖查看。我建構它是因為我需要它。分享它是因為或許你也需要。
你為大數據用戶介面使用過哪些性能技巧?總是好奇那些令人驚訝的技巧。












