大 JSON 解析很慢怎麼優化
結論:優化順序錯了,換什麼函式庫都沒用。先量出時間花在解析、渲染還是請求上,再按「減少解析次數 → 串流處理 → 移到 Worker → 換二進位格式」逐級加碼。

結論:先量出網路、解析與渲染各佔多少時間,多數「解析慢」其實是渲染慢;優化按「減少解析次數 → 拆小回應體 → 串流解析 → 移到 Worker → 換二進位格式」的順序逐級加碼,換函式庫放在最後。
先別換函式庫。多數「解析慢」在加一次 console.time 之後會發現是渲染慢。
第一步:量清楚時間花在哪
const startFetch = performance.now();
const response = await fetch(url);
const text = await response.text();
const startParse = performance.now();
const data = JSON.parse(text);
const startRender = performance.now();
render(data);
分四段記錄:網路、讀流、JSON.parse、渲染。三段耗時占比不同,優化手段完全不同:
| 占比最高的是 | 真正的問題 | 對策 |
|---|---|---|
| 網路 | 回應體太大 | 欄位裁剪、分頁、gzip/brotli |
JSON.parse |
資料量本身大 | 減少解析次數、串流、Worker |
| 渲染 | 一次渲染上萬節點 | 虛擬列表、分頁渲染、折疊 |
JSON.parse 是原生實作,速度不慢:現代瀏覽器解析 1MB JSON 大約 5–15ms,10MB 大約 80–150ms。如果測出來 10MB 要 2 秒,問題多半不在 parse。
第二步:減少解析次數
收益最高、最容易被忽略。
- 別反覆解析同一份資料。 十個元件各自
JSON.parse(localStorage.getItem(...))就是解析十次,提到模組層級快取一次。 - 別用
JSON.parse(JSON.stringify(x))做深拷貝。 代價是資料量的兩倍以上,改用structuredClone。 - 別在主執行緒解析完再傳給 Worker。
postMessage會再複製一次。把原始文字傳給 Worker,讓它自己解析。 - 按需取欄位。 只需要三個欄位時,後端就該裁剪。
第三步:拆小回應體
分頁用 page/size,大資料集用游標而不是 offset,列表介面只回摘要欄位。這一步的效果通常比任何前端優化都大,因為它同時減少了網路、解析與記憶體佔用。
第四步:串流解析
資料必須整體處理、又確實很大時,用串流解析器逐塊處理:
import { parser } from 'stream-json';
import { streamArray } from 'stream-json/streamers/StreamArray';
const pipeline = fs.createReadStream('big.json').pipe(parser()).pipe(streamArray());
for await (const { value } of pipeline) {
await handleOne(value);
}
代價是程式碼複雜度上升,且不能隨機存取——只適合「逐條處理」。
第五步:移到 Worker
解析本身不慢,但會阻塞主執行緒。把解析放進 Web Worker,注意傳的是原始文字,傳已解析的物件會觸發結構化複製,等於把開銷翻倍。
第六步:換格式(最後考慮)
內部系統之間傳輸、體積穩定在幾十 MB 時,可以考慮 NDJSON(一行一條,天生可串流)或 Protobuf/MessagePack。這屬於「換協定」,需要兩端一起改。
別做的事
- 用
eval解析 JSON:安全風險,且不一定更快 - 為了「優化」把 JSON 轉成字串再塞進 JSON:體積翻倍
- 在主執行緒迴圈幾十萬次
JSON.parse小字串:合併成一次解析
常見問題
如何判斷瓶頸在解析還是渲染?
用 performance.now() 把請求、讀流、JSON.parse 與渲染分成四段計時,佔比最高的那一段才是真正的瓶頸。現代瀏覽器解析 1MB JSON 約 5–15ms、10MB 約 80–150ms,如果 10MB 要 2 秒,問題多半不在 parse。
為什麼說換函式庫通常解決不了問題?
JSON.parse 是原生實作,速度不慢,多數「解析慢」來自重複解析或一次渲染上萬節點。換函式庫通常只能帶來 10%–30% 的提升,而減少一次全量解析往往能省一半。
是否應該把解析放進 Web Worker?
當解析阻塞主執行緒、頁面卡住或動畫掉幀時應該放進 Worker。注意要傳原始文字而不是已解析的物件,否則 postMessage 的結構化複製會把開銷翻倍。