Przyspieszanie parsowania dużego JSON
Podsumowanie: Optymalizacja w złej kolejności czyni każdą wymianę biblioteki bezużyteczną. Zmierz osobno sieć, parsowanie i renderowanie, a potem ogranicz parsowania, zmniejsz ładunki i strumieniuj.

Najważniejsze: najpierw zmierz osobno sieć, parsowanie i renderowanie — większość „wolnego parsowania” okazuje się wolnym renderowaniem — a potem wspinaj się po drabinie w kolejności: mniej parsowań, mniejsze ładunki, strumieniowanie, worker i format binarny na końcu.
Nie zaczynaj od wymiany bibliotek. Dodaj jeden timer i zwykle odkryjesz, że problemem jest renderowanie.
Krok pierwszy: zmierz etapy
const t0 = performance.now();
const response = await fetch(url);
const text = await response.text();
const t1 = performance.now();
const data = JSON.parse(text);
const t2 = performance.now();
render(data);
const t3 = performance.now();
| Dominujący etap | Prawdziwy problem | Poprawka |
|---|---|---|
| Sieć | Ładunek za duży | Wybór pól, paginacja, kompresja |
JSON.parse |
Naprawdę duże wejście | Mniej parsowań, strumieniowanie, worker |
| Renderowanie | Dziesiątki tysięcy węzłów naraz | Lista wirtualna, paginacja, zwijanie |
Współczesne przeglądarki parsują 1 MB w około 5–15 ms, a 10 MB w 80–150 ms. Jeśli 10 MB zajmuje dwie sekundy, JSON.parse nie jest twoim wąskim gardłem.
Krok drugi: parsuj mniej
Najtańszy zysk i najbardziej przeoczony.
- Przestań parsować ten sam ładunek wielokrotnie. Dziesięć komponentów, każdy wywołujący
JSON.parse(localStorage.getItem(...)), to dziesięć parsowań. Cache’uj raz na poziomie modułu. - Przestań używać
JSON.parse(JSON.stringify(x))do głębokich kopii. Kosztuje więcej niż dwukrotność objętości danych;structuredClonenie. - Nie parsuj w wątku głównym i nie wysyłaj obiektu do workera.
postMessageklonuje go ponownie. Wyślij surowy tekst i pozwól workerowi parsować. - Proś o mniej pól. Jeśli UI potrzebuje trzech właściwości, API nie powinno wysyłać trzystu.
Krok trzeci: zmniejsz ładunek
Paginacja przez page/size, kursory zamiast głębokiego offset, pola podsumowujące w punktach końcowych list. Zwykle bije to wszystkie optymalizacje frontendu razem, bo redukuje sieć, parsowanie i pamięć naraz.
Krok czwarty: strumieniuj
Gdy dane muszą być przetworzone jako całość i naprawdę są duże:
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);
}
Więcej kodu i brak dostępu swobodnego — pasuje tylko do przetwarzania rekord po rekordzie.
Krok piąty: przenieś poza wątek główny
Parsowanie jest szybkie, ale blokujące. Umieść je w workerze i przekazuj tekst, a nie sparsowany obiekt, inaczej klonowanie strukturalne podwaja koszt.
Krok szósty: zmień format, na końcu
Dla ruchu wewnętrznego, który konsekwentnie liczy dziesiątki megabajtów, NDJSON strumieniuje naturalnie, a Protobuf lub MessagePack są mniejsze i szybsze. Oba są zmianami protokołu wymagającymi koordynacji.
Czego nie robić
evalna JSON-ie: niebezpieczne i nie szybsze- Stringify JSON, aby osadzić go w JSON-ie: podwaja rozmiar
- Parsowanie setek tysięcy malutkich łańcuchów w pętli: łącz je partiami
FAQ
Jak rozpoznać, czy wąskim gardłem jest parsowanie czy renderowanie?
Zmierz osobno czas żądania, odczytu tekstu, JSON.parse i renderowania przez performance.now() i traktuj największy etap jako prawdziwe wąskie gardło. Współczesne przeglądarki parsują 1 MB w około 5–15 ms, a 10 MB w 80–150 ms, więc 10-megabajtowy ładunek zajmujący dwie sekundy rzadko jest problemem JSON.parse.
Dlaczego wymiana biblioteki zwykle nie pomaga?
JSON.parse jest już natywne i szybkie; większość „wolnego parsowania” pochodzi z powtarzanego parsowania lub renderowania dziesiątek tysięcy węzłów naraz. Wymiana biblioteki kupuje 10–30%, podczas gdy usunięcie jednego pełnego parsowania często oszczędza połowę.
Czy przenieść parsowanie do Web Workera?
Przenieś je do workera, gdy parsowanie blokuje wątek główny i strona się zawiesza lub animacje gubią klatki. Przekazuj surowy tekst, a nie sparsowany obiekt, inaczej klonowanie strukturalne postMessage podwaja koszt.