Code splitting: чтение кода
Читай реальные React/JS-сниппеты разбивки — границу dynamic import, раскладку lazy+Suspense и обработчик prefetch-on-hover — и выбирай поведение или самый высокорычажный фикс.
Баги разбивки живут в том, где стоит граница и когда запрашивается чанк. Читай каждый сниппет, прослеживай тайминг запросов, который браузер реально выполняет, и выбирай фикс, который senior сделает первым.
Потренируй чтение точек разбивки так, как ты их дебажишь в проде: найди, где реально стоит граница dynamic import, увидь, когда Suspense триггерит загрузку, и заметь waterfall, который упредил бы hint.
Сниппет 1 — где стоит граница
// A: импорт на верхнем уровне модуля — бандлится в родительский чанк
import { HeavyEditor } from "./HeavyEditor";
function EditPanel({ open }) {
if (!open) return null;
return <HeavyEditor />;
}
// B: dynamic import внутри гейта
const HeavyEditor = React.lazy(() => import("./HeavyEditor"));
function EditPanel({ open }) {
if (!open) return null;
return (
<Suspense fallback={<Spinner />}>
<HeavyEditor />
</Suspense>
);
}
Редактор весит 200 KB и открывается только по клику на Edit. Что реально отгружает каждая версия и какая здесь правильная?
Сниппет 2 — вложенные границы Suspense
const Dashboard = React.lazy(() => import("./Dashboard"));
const Chart = React.lazy(() => import("./Chart"));
function Page() {
return (
<Suspense fallback={<PageSkeleton />}>
<Dashboard>
<Suspense fallback={<ChartSpinner />}>
<Chart />
</Suspense>
</Dashboard>
</Suspense>
);
}
На телефоне с RTT 150 ms в каком порядке загружаются чанки Dashboard и Chart и почему это важно?
Сниппет 3 — prefetch по hover
const Settings = React.lazy(() => import("./Settings"));
function NavLink() {
return (
<a
href="/settings"
onMouseEnter={() => import("./Settings")}
>
Settings
</a>
);
}
Что делает обработчик onMouseEnter и какой failure mode, если его убрать?
Три вещи решают поведение разбивки в коде: статический верхнеуровневый импорт всегда бандлится в родительский чанк, поэтому только import() создаёт точку разбивки — и runtime-условие этого не меняет. Вложенные границы React.lazy обнаруживаются последовательно, поэтому каждый уровень добавляет round trip на канале с высоким RTT. А запуск import() заранее (на hover или idle) прогревает кэш чанка, чтобы итоговое монтирование пропустило waterfall и спиннер. Читай, где стоит граница и когда триггерится загрузка, затем сдвигай триггер раньше, а не сливай чанк обратно. Теперь, когда видишь в PR вложенные React.lazy-компоненты, — первым делом проверь, стоят ли эти границы последовательно на критическом пути: если да, попроси preload-хинт или плоскую границу до мержа.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.