Middle JavaScript и React
Экспресс-шпаргалка 20/20
- React сравнивает предыдущее и новое virtual DOM дерево и применяет в real DOM только минимально необходимые изменения.
keyнужен, чтобы React стабильно идентифицировал элементы между рендерами и корректно обновлял список.- В зависимости нужно включать все внешние значения, которые используются внутри effect, чтобы избежать stale данных.
- Они полезны, когда есть измеримая проблема производительности: дорогие вычисления или лишние ререндеры из-за нестабильных ссылок.
- Контролируемая форма хранит значение в state, неконтролируемая — в DOM через ref.
- State поднимают к ближайшему общему родителю, если нескольким компонентам нужны одни и те же данные.
- При изменении value контекста все подписанные потребители могут перерендериться.
- Error Boundary ловит ошибки рендера в дочернем дереве React-компонентов и показывает fallback UI.
- Для code splitting и отложенной загрузки тяжелых частей интерфейса.
- Нестабильные props (функции/объекты), слишком высокий state, изменения контекста и отсутствие мемоизации в нужных местах.
- CSR рендерит в браузере, SSR рендерит HTML на сервере на каждый запрос, SSG генерирует HTML заранее на этапе сборки.
- Hydration mismatch — несоответствие между HTML с сервера и первым рендером клиента.
- Оба описывают форму данных, но
interfaceпроще расширять через declaration merging, аtypeгибче для union/intersection и утилитарных комбинаций. - Generics позволяют писать переиспользуемые функции/типы с сохранением строгой типизации.
- Это union объектов с общим дискриминатором (например,
type), который позволяет TypeScript безопасно сужать тип. - Чаще всего
Partial,Pick,Omit,Record,Readonly,ReturnType. Promise.allпадает на первой ошибке,Promise.allSettledдожидается завершения всех промисов и возвращает статус каждого.AbortControllerпозволяет отменятьfetchи другие abortable операции, чтобы не держать лишние запросы и состояния.- Это обработка событий на общем родителе вместо подписки на каждый дочерний элемент.
- Tree shaking удаляет неиспользуемые экспорты из бандла, если код и сборка позволяют это статически определить.
1. Как работает reconciliation в React?
Теги: react, reconciliation, rendering, performance
Сложность: Middle
Короткий ответ
Reconciliation - это сопоставление прошлого и нового React-дерева, после которого React решает, что обновить, сохранить или размонтировать.
Пояснение
В реальных frontend-интервью этот вопрос проверяет не знание слова "virtual DOM", а понимание identity компонента. React хранит state не "в JSX", а в позиции компонента в дереве. Если на той же позиции остается тот же тип компонента, React обычно сохраняет state и обновляет props. Если меняется тип или key, старое поддерево считается другим: cleanup эффектов, remount, потеря локального state, повторная инициализация.
Главная практическая мысль: rerender не равен remount. Rerender - это повторный вызов компонента для расчета нового UI. Remount - это уничтожение старого экземпляра и создание нового. На интервью сильный кандидат объясняет, как это проявится в форме, таблице, списке, модалке или wizard flow, а не только говорит "React сравнивает деревья".
Что сказать на интервью (30-60 секунд)
- Я бы начал с identity: React сопоставляет элементы по типу, позиции в дереве и
key. - Если identity сохранилась, React обновляет props/state и идет глубже; если identity изменилась, поддерево сбрасывается.
- Поэтому важно различать rerender, DOM update и remount: это разные события с разной ценой.
- На практике я проверяю reconciliation через render/remount trace, особенно в формах, списках и wizard flows.
- Красный флаг - случайные
key, условные wrapper-компоненты и вложенные component definitions, которые случайно сбрасывают state.
Мини-пример
function CheckoutStep({ step }) {
return step === 'delivery'
? <DeliveryForm />
: <PaymentForm />;
}
Если пользователь вводил данные в DeliveryForm, а UI переключился на другой тип компонента в той же позиции, старый form state будет уничтожен. Если нужно сохранить draft между шагами, state должен жить выше или сохраняться отдельно.
Углубление (2-3 минуты)
- Production-кейс: в checkout wizard пользователь заполнил адрес, потом переключил способ доставки. Из-за смены
keyу wrapper-компонента форма размонтировалась, draft пропал, и пользователь вынужден повторно вводить данные. - Edge-case:
key={Math.random()}делает каждый render новым деревом, поэтому React пересоздает DOM, сбрасывает input и заново запускает эффекты. - Edge-case: изменение props у того же компонента не сбрасывает state само по себе; если нужен reset, его делают явно через
keyили controlled state. - Readiness: вы можете объяснить, где state сохранится, где сбросится, и какой user-visible bug появится.
Практика
Сделайте render trace для Parent -> List -> Row: action, component, rendered, mounted/unmounted, state preserved, why. Отдельно добавьте сценарий с key={Math.random()} и объясните, почему он ломает ввод.
Типичные ошибки
- Говорят, что virtual DOM "быстрее real DOM" без объяснения сопоставления деревьев.
- Путают rerender, DOM update и remount.
- Ставят случайный
keyили индекс там, где порядок списка меняется. - Не могут объяснить, почему state формы исчезает при смене
key.
Follow-up вопросы
- Чем отличается rerender от remount?
- Что будет со state дочернего компонента, если поменять его
key? - Почему React не может надежно сопоставить элементы списка без стабильного
key? - Как бы вы нашли лишние remount в форме или таблице?
Что повторить
- React lesson 1: render, reconciliation,
key. - Мосты практики:
React rendering and effects.
Связанные модули и карта
2. Зачем нужен key в списках?
Теги: react, key, lists, state-preservation
Сложность: Middle
Короткий ответ
key нужен, чтобы React узнавал один и тот же элемент списка между рендерами, даже если элементы добавили, удалили, отфильтровали или отсортировали.
Пояснение
key - это не "ускоритель map" и не props для компонента. Это подсказка React внутри массива siblings: какой новый элемент соответствует какому старому элементу. Если ключ стабильный и взят из данных, React может сохранить локальный state нужной строки. Если ключ основан на индексе в изменяемом списке, React может связать старый state не с тем item.
Типичный интервью-паттерн: интервьюер дает список с input внутри строки, потом просит удалить первый элемент или отсортировать список. Слабый ответ - "индекс нельзя". Сильный ответ - показать, как state/focus/draft переезжает к другому item и когда индекс все-таки безопасен.
Что сказать на интервью (30-60 секунд)
keyдолжен выражать identity элемента в данных:todo.id,message.id,user.id.- Он уникален только среди соседей одного массива, а не глобально во всем приложении.
- Индекс как
keyопасен, когда список меняет порядок, фильтруется, пополняется или содержит локальный state. - Если список статичный, не сортируется и не имеет stateful children, индекс почти не несет риска.
- Я всегда проверяю
keyчерез сценарий: удалить первый item, отсортировать список, отредактировать input в середине.
Мини-пример
tasks.map(task => (
<TaskRow key={task.id} task={task} />
));
Если вместо task.id использовать индекс, после сортировки draft text, focus или animation state могут остаться на позиции строки, но уже относиться к другой задаче.
Углубление (2-3 минуты)
- Production-кейс: оператор редактирует цену второй позиции заказа. В это время список автоматически сортируется по скидке. При
key={index}введенное значение может визуально оказаться у другой позиции. - Edge-case: короткий список статичных пунктов меню без reorder, filter, insert и локального state можно рендерить с индексом, но это должно быть осознанное ограничение.
- Edge-case:
keyне приходит в child как prop; если нужен id внутриTaskRow, его надо передать отдельно. - Readiness: вы можете не только сказать "индекс плохо", а доказать, какой state сохранится неправильно.
Практика
Нарисуйте таблицу для списка с тремя строками и input: old index, old id, draft value, new index after delete/sort, which row receives draft. Повторите таблицу для key={id} и key={index}.
Типичные ошибки
- Считают
keyспособом "ускорить map", а не identity для reconciliation. - Используют
Math.random()илиDate.now()какkey. - Используют индекс в сортируемой таблице, форме, drag-and-drop или списке уведомлений.
- Не могут назвать условия, при которых индекс допустим.
Follow-up вопросы
- Почему
keyдолжен быть уникален только среди siblings, а не глобально во всем приложении? - Что произойдет при
key={index}, если удалить первый элемент списка? - Где должен стоять
key: внутри child-компонента или вmap? - Когда индекс как
keyприемлем?
Что повторить
- React lesson 1: списки,
key, сохранение state. - Interview pattern taxonomy:
render-ownership.
Связанные модули и карта
3. Как правильно работать с зависимостями в useEffect?
Теги: react, useeffect, dependencies, async
Сложность: Middle
Короткий ответ
В dependencies нужно указывать все reactive значения из компонента, которые effect читает: props, state и функции/переменные из render scope.
Пояснение
useEffect нужен для синхронизации с внешним миром: сеть, subscription, timer, DOM API, сторонний widget. Dependency array не является ручным расписанием "когда хочу, тогда запускаю"; он должен соответствовать коду внутри effect. Если effect читает query, userId, token или callback, а dependency отсутствует, effect работает со старым snapshot значения.
В интервью обычно проверяют два риска. Первый - stale closure: effect видит старые данные. Второй - cleanup: старый запрос, таймер или подписка продолжает жить после изменения входных данных. Поэтому хороший ответ всегда включает не только dependencies, но и отмену/игнорирование старого результата.
Что сказать на интервью (30-60 секунд)
- Я сначала спрашиваю: effect точно нужен или это derived value, который можно посчитать во время render?
- Если effect синхронизирует внешний ресурс, dependencies должны отражать все значения, которые он читает.
- Для async важно не только перезапустить effect, но и отменить или проигнорировать устаревший результат.
[]подходит только когда effect действительно не зависит от меняющихся props/state, а не как способ отключить линтер.- На практике я доказываю корректность через сценарий быстрых изменений:
a -> ab -> abc, старый ответ приходит последним.
Мини-пример
useEffect(() => {
const controller = new AbortController();
fetchUser(userId, { signal: controller.signal }).then(setUser);
return () => controller.abort();
}, [userId]);
Здесь userId в dependencies обязателен: при смене пользователя старый запрос отменится, а новый effect запросит актуальные данные.
Углубление (2-3 минуты)
- Production-кейс: autocomplete отправляет запросы на
a,ab,abc. Ответ наaприходит позже всех и перетирает экран устаревшими результатами. - Edge-case: если callback создается на каждом render и попадает в dependencies, effect может запускаться слишком часто. Решение - перенести функцию внутрь effect, стабилизировать callback или убрать лишний effect.
- Edge-case: subscription и interval требуют cleanup, иначе после смены страницы останутся лишние listeners или таймеры.
- Readiness: вы можете назвать stale переменную, момент запуска cleanup и способ защиты от устаревшего async результата.
Практика
Сделайте dependency audit для компонента с query, token, setTimeout, fetch и window.addEventListener. Для каждой строки заполните: external value, dependency needed, cleanup, stale failure, fix.
Типичные ошибки
- Убирают dependency, чтобы "не было лишнего запроса", и получают stale данные.
- Кладут в effect derived state, который можно вычислить во время render.
- Не чистят subscription, interval, timeout или network request.
- Лечат двойной запуск в development через
useRef, но не исправляют cleanup.
Follow-up вопросы
- Почему нельзя просто удалить
queryиз dependencies? - Когда cleanup запускается относительно следующего effect?
- Что выбрать для stale response:
AbortController,requestIdили проверку актуального значения? - Какие вычисления лучше оставить в render, а не переносить в
useEffect?
Что повторить
- React lesson 2: lifecycle effect, cleanup, dependencies.
- React effect/state debug pack: stale response drill.
Связанные модули и карта
4. Когда useMemo и useCallback реально полезны?
Теги: react, usememo, usecallback, optimization
Сложность: Middle
Короткий ответ
useMemo и useCallback полезны, когда есть измеримая цена: дорогое вычисление, memoized child, стабильная dependency или большой список.
Пояснение
Эти хуки не "ускоряют React" автоматически. useMemo сохраняет результат вычисления между render, пока dependencies те же. useCallback сохраняет ссылку на функцию. Польза появляется только если эта стабильность где-то используется: например, тяжелый filter не должен пересчитываться на каждый keypress, или memoized row не должен получать новый onSelect без изменения данных.
Сильный ответ на интервью начинается не с "я оборачиваю все в memo", а с диагностики: что именно дорого, как это измерили, какой render лишний, какой dependency нестабилен, и какой UX-баг или latency это создает.
Что сказать на интервью (30-60 секунд)
- Я использую memoization после baseline: Profiler, render count или простой замер времени вычисления.
useMemo- для результата дорогого вычисления;useCallback- для стабильной ссылки, если consumer реально сравнивает ссылку.- Если child не memoized и callback никуда не уходит как dependency,
useCallbackобычно не дает пользы. - Если dependency каждый render новый, memo будет инвалидироваться каждый render.
- Главный риск - stale dependencies и усложнение кода ради микропользы.
Мини-пример
const filtered = useMemo(() => heavyFilter(items, query), [items, query]);
const onSelect = useCallback(id => setSelected(id), []);
Этот пример имеет смысл, только если heavyFilter действительно дорогой или onSelect передается в memoized rows. Иначе это может быть лишней сложностью.
Углубление (2-3 минуты)
- Production-кейс: CRM-таблица на 2 000 строк фильтруется при каждом вводе символа, а строка содержит форматирование валюты, статусы и actions. Без baseline команда спорит "медленно React или API"; с baseline видно, что тормозит client render.
- Edge-case: если родитель каждый раз создает новый
items.map(...)и передает его ниже,useMemo([items])внутри ребенка не поможет. - Edge-case: если забыть dependency, UI может показывать старый filtered list, что хуже лишнего render.
- Readiness: вы можете сказать, где memo нужен, где ничего не меняет, и чем это доказано.
Практика
Заполните memoization baseline table: operation, input size, current cost, rerender trigger, memo candidate, dependency risk, proof after fix. Не добавляйте memo, пока нет строки proof.
Типичные ошибки
- Оборачивают каждую функцию в
useCallbackбез memoized consumer. - Забывают dependency и получают stale value.
- Мемоизируют дешевое вычисление, но оставляют высокий state, который ререндерит все дерево.
- Ожидают, что
useMemoпредотвратит render компонента.
Follow-up вопросы
- Чем
useMemoотличается отReact.memo? - Когда
useCallbackреально нужен? - Почему memo может не сработать, если dependency каждый раз новая?
- Что опаснее: лишний render или stale значение из-за неверных dependencies?
Что повторить
- React lesson 3: state, derived data, memoization.
- Interview pattern taxonomy:
performance-budgetandrender-ownership.
Связанные модули и карта
5. Контролируемые и неконтролируемые формы в React?
Теги: react, forms, controlled-uncontrolled, validation
Сложность: Middle
Короткий ответ
В controlled input source of truth - React state; в uncontrolled input source of truth - DOM, а React читает значение через ref или submit.
Пояснение
Этот вопрос проверяет умение выбрать ownership формы. Controlled подход нужен, когда UI зависит от текущего значения: live validation, маски, disabled submit, зависимые поля, autosave, синхронизация с query/state. Uncontrolled подход проще, когда значение нужно только при submit, когда поле тяжелое для постоянного render или когда API браузера не дает нормально контролировать значение, как у file input.
Сильный ответ не должен звучать как "controlled всегда лучше". В реальном проекте выбор зависит от validation timing, размера формы, UX-стоимости render на каждый keypress и того, где живет business state.
Что сказать на интервью (30-60 секунд)
- Я выбираю controlled, если значение должно управлять UI прямо сейчас: validation, mask, disabled button, dependent field.
- Я выбираю uncontrolled, если значение нужно считать в конце или если поле лучше оставить под управлением DOM.
- Для controlled input обязательны
valueиonChange, которые синхронно обновляют backing state. - Нельзя переключать input между controlled и uncontrolled в течение жизни компонента.
- Риск не в самом подходе, а в рассинхроне source of truth: UI показывает одно, validation читает другое.
Мини-пример
<input value={name} onChange={e => setName(e.target.value)} />
Если убрать onChange, поле станет read-only. Если иногда передавать undefined вместо строки, React может считать input то controlled, то uncontrolled.
Углубление (2-3 минуты)
- Production-кейс: форма настроек включает submit только после валидного email и выбранного тарифа. Controlled state делает правила явными и тестируемыми.
- Edge-case:
<input value={maybeName}>, гдеmaybeNameиногдаundefined, может дать controlled/uncontrolled warning. Для controlled text input держите строку, напримерvalue={name ?? ''}. - Edge-case: checkbox контролируется через
checked, а не черезvalue; file input читают через DOM/ref. - Edge-case: большая форма может лагать, если весь экран зависит от каждого keypress; тогда state надо локализовать, разбить форму или использовать deferred подход.
- Readiness: вы можете выбрать подход по source of truth, validation timing и UX-cost, а не по привычке.
Практика
Сделайте form failure table для login/settings form: field, controlled/uncontrolled, source of truth, validation timing, failure mode, fix. Обязательно включите text input, checkbox и file input.
Типичные ошибки
- Используют
valueбезonChangeи получают readonly input. - Передают
undefined/nullвvalueи ловят controlled/uncontrolled warning. - Делают всю большую форму controlled, хотя live state нужен только нескольким полям.
- Смешивают DOM ref и React state так, что validation читает одно значение, а UI показывает другое.
Follow-up вопросы
- Что является source of truth в controlled input?
- Почему React ругается на переход controlled -> uncontrolled?
- Как обработать
file input? - Когда uncontrolled форма лучше controlled?
Что повторить
- React lesson 2: forms, effects, controlled inputs.
- Мосты практики:
React rendering and effects.
Связанные модули и карта
6. Когда поднимать state вверх (lifting state up)?
Теги: react, state-management, lifting-state, architecture
Сложность: Middle
Короткий ответ
State поднимают к ближайшему общему родителю, если нескольким компонентам нужны одни и те же данные.
Что сказать на интервью (30-60 секунд)
- State должен жить у ближайшего общего владельца, которому нужно читать или менять это значение.
- Если значение нужно только одному input или dropdown, оставляю state локально; если List и Details должны синхронизироваться, поднимаю к их общему parent.
- Derived data не обязательно хранить в state: фильтрованный список часто можно вычислить из
itemsиquery. - Production-риск: два источника правды дают рассинхрон, а слишком высокий state заставляет ререндерить большое дерево.
- Проверяю через state ownership map:
state,owner,readers,writers,derived?,rerender cost.
Мини-пример
// Parent хранит selectedId и передает вниз в List и Details
Углубление (2-3 минуты)
- Production-сценарий:
ProductListиProductDetailsдолжны видеть одинselectedId. Если каждый хранит свой selected state, пользователь видит один товар в списке и другой в деталях. - Edge-case:
isDropdownOpenдля одной строки не нужно поднимать к странице, иначе открытие dropdown может ререндерить весь список. - Практика: для filterable list заполните ownership map:
query,selectedId,items,filteredItems,isRowMenuOpen. - Readiness: вы можете объяснить, что должно быть state, что derived data, а что server/cache data вне локального component state.
Типичные ошибки
- Поднимают весь state "на всякий случай" и получают prop drilling с лишними rerender.
- Дублируют одно значение в parent и child, а потом синхронизируют через effect.
- Хранят derived data в state и забывают обновить его при изменении исходных данных.
- Путают state ownership с глобальным store: не каждый shared state должен стать global.
Follow-up вопросы
- Как выбрать ближайшего общего владельца state?
- Когда derived value лучше не хранить в state?
- Почему поднятый слишком высоко state может ухудшить performance?
- Как избежать двух источников правды между parent и child?
Что повторить
- React lesson 3: state ownership and derived state.
- Мосты практики:
React state and performance. - Free-practice: state ownership map for filterable list.
Связанные модули и карта
7. Какие проблемы у Context API по производительности?
Теги: react, context-api, performance, state
Сложность: Middle
Короткий ответ
При изменении value контекста все подписанные потребители могут перерендериться.
Что сказать на интервью (30-60 секунд)
- Context удобен для данных, которые нужны многим веткам: theme, locale, auth session, feature flags.
- Когда
valueprovider меняет identity, все consumers этого context получают новый value и могут перерендериться. useMemoпомогает только стабилизировать объектvalue, но не решает проблему слишком широкого context.- Для часто меняющихся данных лучше split contexts, локальный state ближе к consumer, external store или selector-based подход.
- Проверяю через consumer trace: какой provider изменился, какие consumers перерендерились, какой field им реально нужен.
Мини-пример
const value = useMemo(() => ({ theme, setTheme }), [theme]);
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
Углубление (2-3 минуты)
- Production-сценарий: в
AppContextлежатtheme,user,cart,searchDraft. При каждом keypress в search перерендериваются header, cart badge и страницы, которым search не нужен. - Edge-case:
const value = { theme, setTheme }создает новый объект на каждом render provider, даже еслиthemeне изменился. - Практика: сделайте Context consumer trace:
provider value,changed field,consumer,needed field,rerender expected?,fix. - Readiness: вы можете выбрать между split context, memoized value, moving state down и external store, а не просто сказать "используйте Redux".
Типичные ошибки
- Складывают в один context и редко меняющийся
theme, и быстро меняющийсяsearchDraft. - Думают, что
React.memoвсегда спасет consumer от изменения context. - Мемоизируют
value, но оставляют нестабильные вложенные callbacks или objects. - Используют Context как универсальный global store без границ ownership.
Follow-up вопросы
- Что происходит с consumers при изменении
valueprovider? - Почему
useMemo(() => ({ theme }), [theme])не решает проблему частых измененийtheme? - Когда split context лучше одного общего context?
- Почему Context плохо подходит для rapidly changing input state?
Что повторить
- React lesson 4: Context limits and performance.
- Interview pattern taxonomy:
render-ownership. - Free-practice: Context consumer trace for
ThemeContext,AuthContext,SearchContext.
Связанные модули и карта
8. Что такое Error Boundary и где его ограничения?
Теги: react, error-boundary, resilience, monitoring
Сложность: Middle
Короткий ответ
Error Boundary ловит ошибки рендера в дочернем дереве React-компонентов и показывает fallback UI.
Что сказать на интервью (30-60 секунд)
- Error Boundary ловит ошибки во время render, lifecycle и constructor в дочернем дереве компонентов; сам boundary обычно реализуют class-компонентом.
- Он не ловит ошибки в event handlers, async callbacks,
setTimeout, rejected promises и серверном рендере. - Boundary нужен, чтобы изолировать падение части UI, показать fallback и отправить ошибку в monitoring.
- Размещаю boundaries по пользовательским зонам: виджет, route, checkout step, editor panel, а не только один глобальный wrapper.
- Проверяю placement table:
area,failure,fallback,reset action,monitoring signal.
Мини-пример
<ErrorBoundary fallback={<ErrorScreen />}>
<App />
</ErrorBoundary>
Углубление (2-3 минуты)
- Production-сценарий: recommendations widget падает из-за неожиданных данных. Boundary вокруг widget сохраняет checkout page живой и показывает локальный fallback.
- Edge-case: ошибка внутри
onClickне попадет в Error Boundary; ее нужно обработать в handler или через async state. - Edge-case: ошибка загрузки lazy chunk обычно требует связки
Suspenseдля loading и Error Boundary для failed import. - Практика: заполните boundary placement table для
AppShell,CheckoutStep,ProductReviews,RichTextEditor. - Readiness: вы можете объяснить, какую ошибку boundary поймает, какую нет, и как пользователь восстановится через reset.
Типичные ошибки
- Считают Error Boundary заменой
try/catchдля event handler. - Ставят только один глобальный boundary и теряют весь экран при локальной ошибке.
- Показывают fallback без кнопки retry/reset и без сигнала в monitoring.
- Не тестируют failed lazy import, хотя именно там пользователи часто видят пустой экран.
Follow-up вопросы
- Какие ошибки Error Boundary не ловит?
- Где поставить boundary в dashboard из независимых widgets?
- Как сбросить boundary после ошибки?
- Чем fallback для render error отличается от loading fallback в
Suspense?
Что повторить
- React lesson 4: resilience boundaries.
- Мосты практики:
React state and performance. - Free-practice: boundary placement table with fallback and reset behavior.
Связанные модули и карта
9. Когда использовать React.lazy и Suspense?
Теги: react, lazy, suspense, code-splitting
Сложность: Middle
Короткий ответ
Для code splitting и отложенной загрузки тяжелых частей интерфейса.
Что сказать на интервью (30-60 секунд)
React.lazyзагружает компонент через dynamicimport()и выносит его в отдельный chunk.Suspenseпоказывает fallback, пока lazy component или другая suspense-enabled dependency не готова.- Использую это для тяжелых route-level страниц, вкладок, модалок, редакторов, графиков и редко открываемых настроек.
- Не выношу в lazy критический above-the-fold UI, если fallback ухудшит первый экран или добавит сетевой waterfall.
- Проверяю lazy chunk decision table:
component,initially visible?,size/cost,load trigger,fallback,error handling.
Мини-пример
const Settings = React.lazy(() => import('./Settings'));
<Suspense fallback={<SettingsSkeleton />}>
<Settings />
</Suspense>
Углубление (2-3 минуты)
- Production-сценарий: settings modal содержит rich editor и charts. Его можно загружать по клику, чтобы не тащить этот код в initial bundle.
- Edge-case: если lazy chunk нужен сразу для hero area, пользователь увидит skeleton вместо основного контента и может ухудшиться perceived load.
- Edge-case: failed dynamic import не решается
Suspensefallback; нужен Error Boundary рядом. - Практика: заполните lazy chunk decision table для
Dashboard,SettingsModal,ChartPanel,CheckoutButton. - Readiness: вы можете объяснить trade-off между меньшим initial JS и дополнительным network request при открытии lazy части.
Типичные ошибки
- Lazy-load маленьких компонентов, где overhead запроса больше выгоды.
- Ставят fallback, который меняет layout и вызывает визуальный скачок.
- Забывают Error Boundary для failed chunk.
- Делают lazy import внутри render branch нестабильно вместо module scope.
Follow-up вопросы
- Чем
React.lazyотличается от обычного static import? - Почему
Suspensefallback не обрабатывает ошибку загрузки chunk? - Что лучше lazy-load: route page, modal или кнопку в первом экране?
- Как избежать layout shift у fallback?
Что повторить
- React lesson 4: code splitting and loading states.
- Interview pattern taxonomy:
performance-budget. - Free-practice: lazy chunk decision table for one real page.
Связанные модули и карта
10. Какие основные причины лишних ререндеров в React?
Теги: react, rerender, profiler, optimization
Сложность: Middle
Короткий ответ
Нестабильные props (функции/объекты), слишком высокий state, изменения контекста и отсутствие мемоизации в нужных местах.
Что сказать на интервью (30-60 секунд)
- Компонент ререндерится, когда меняется его state, props, context или когда ререндерится parent и child не защищен memoization.
- Частые причины: высокий state, нестабильные object/function props, широкий context, дорогой derived data in render, неправильные
key. - Не каждый rerender проблема: проблема начинается, когда он дорогой, частый или ломает UX.
- Диагностика идет от evidence: React DevTools Profiler, render trace, список props/context changes, затем минимальный fix.
- Fix выбираю по причине: move state down, split context, stabilize props,
React.memo,useMemo,useCallback, virtualization.
Мини-пример
const onClick = () => setOpen(true); // новая ссылка каждый render
Углубление (2-3 минуты)
- Production-сценарий: ввод в search field ререндерит таблицу на 2 000 строк, потому что
queryлежит в page state, аrowsполучают новыйonSelectна каждый render. - Edge-case: если child дешевый и не memoized,
useCallbackдля handler может ничего не улучшить. - Edge-case: изменение
keyможет выглядеть как rerender, но фактически это remount и сброс state. - Практика: заполните rerender diagnosis table:
interaction,component,trigger,evidence,fix,risk. - Readiness: вы можете назвать причину конкретного rerender и выбрать targeted fix, а не добавлять memo everywhere.
Типичные ошибки
- Оптимизируют все ререндеры, даже дешевые и редкие.
- Добавляют
useCallbackбезReact.memoили другого потребителя стабильной ссылки. - Не замечают, что context value меняется и пробивает memoized children.
- Лечат rerender мемоизацией, хотя правильнее опустить state ниже.
Follow-up вопросы
- Какие четыре события чаще всего запускают rerender?
- Чем rerender отличается от remount?
- Когда
React.memoне поможет? - Как понять, что state нужно опустить ниже, а не мемоизировать callbacks?
Что повторить
- React lesson 3: state, memoization and derived data.
- Interview pattern taxonomy:
render-ownershipandperformance-budget. - Free-practice: rerender diagnosis table for filterable list or dashboard widgets.
Связанные модули и карта
11. Разница между CSR, SSR и SSG?
Теги: frontend, csr-ssr-ssg, rendering-strategy, seo
Сложность: Middle
Короткий ответ
CSR рендерит в браузере, SSR рендерит HTML на сервере на каждый запрос, SSG генерирует HTML заранее на этапе сборки.
Что сказать на интервью (30-60 секунд)
- CSR отдает почти пустой HTML и собирает UI в браузере; сильная сторона - интерактивные кабинеты, слабая - первый контент и SEO без дополнительных мер.
- SSR генерирует HTML на сервере на каждый запрос; полезен для динамического контента, но добавляет server cost, cache strategy и риск hydration issues.
- SSG генерирует HTML на build time; хорош для стабильных страниц, документации и маркетинга, но требует стратегии обновления данных.
- Выбор зависит от freshness, SEO, personalized data, first paint, cacheability and runtime cost.
- Проверяю через rendering strategy decision table:
page,data freshness,SEO,personalization,render mode,fallback/update strategy.
Мини-пример
Next.js: static page + dynamic route with server rendering
Углубление (2-3 минуты)
- Production-сценарий: публичная статья подходит для SSG, каталог с ценами может требовать SSR или revalidation, личный кабинет часто остается CSR после auth.
- Edge-case: страница может быть статичной по shell, но загружать персональные данные на клиенте после auth.
- Edge-case: SSR не делает страницу интерактивной сам по себе; после HTML всё равно нужна hydration.
- Практика: заполните decision table для
landing,blog article,product page,user dashboard,admin settings. - Readiness: вы можете объяснить не "SSR быстрее", а какой trade-off вы выбираете для freshness, SEO, cache и interactivity.
Типичные ошибки
- Называют SSR универсально лучшим вариантом без учета cache и server cost.
- Путают "HTML есть на сервере" и "страница уже интерактивна".
- Выбирают SSG для данных, которые должны быть актуальны на каждый запрос, без revalidation plan.
- Переносят private user data в HTML, который может быть закеширован неправильно.
Follow-up вопросы
- Чем SSR отличается от SSG по моменту генерации HTML?
- Почему CSR может быть нормальным выбором для dashboard?
- Что должно быть true, чтобы SSG был безопасным для страницы?
- Где появляется hydration в SSR-приложении?
Что повторить
- Rendering lesson 1: rendering strategies and critical path.
- Мосты практики:
HTML, CSS and rendering. - Free-practice: rendering strategy decision table for five page types.
Связанные модули и карта
12. Что такое hydration mismatch и как его избежать?
Теги: frontend, hydration, ssr, rendering
Сложность: Middle
Короткий ответ
Hydration mismatch — несоответствие между HTML с сервера и первым рендером клиента.
Что сказать на интервью (30-60 секунд)
- Hydration ожидает, что серверный HTML совпадет с первым клиентским render.
- Mismatch появляется из-за nondeterministic values:
Date.now(),Math.random(), locale/timezone differences, browser-only state, auth state, viewport checks. - Исправление: сделать первый render deterministic, перенести browser-only часть в effect, передать initial data с сервера или явно разделить client-only UI.
- Production-риск: пользователь видит warning, UI пересобирается, может мигнуть текст, потеряться focus или сломаться обработчики.
- Проверяю через hydration audit:
server value,first client value,source of difference,fix,verification.
Мини-пример
// Плохо: рендерим Date.now() напрямую в SSR markup
Углубление (2-3 минуты)
- Production-сценарий: сервер рендерит "0 товаров", а клиент сразу читает cart из
localStorageи рендерит "3 товара"; первый клиентский render не совпадает с HTML. - Edge-case:
new Date().toLocaleString()может дать разный результат на сервере и клиенте из-за timezone/locale. - Edge-case: проверка
window.innerWidthво время render невозможна на сервере и должна быть отложена или заменена CSS/responsive approach. - Практика: сделайте hydration mismatch audit для
Date.now(),localStorage cart,theme from system,viewport-dependent menu. - Readiness: вы можете объяснить, почему
useEffectменяет UI уже после hydration, а не исправляет первый HTML.
Типичные ошибки
- Рендерят случайные значения или текущую дату прямо в SSR markup.
- Читают
window,document,localStorageво время render. - Скрывают warning, не устраняя несовпадение первого render.
- Дублируют логику форматирования даты на сервере и клиенте с разными locale/timezone settings.
Follow-up вопросы
- Почему
Date.now()в render опасен для SSR? - Как безопасно показать значение из
localStorage? - Чем client-only component отличается от deterministic SSR render?
- Как проверить, что hydration warning исчез не случайно?
Что повторить
- Rendering lesson 2: hydration and browser/client boundary.
- Мосты практики:
HTML, CSS and rendering. - Free-practice: hydration mismatch audit for four nondeterministic values.
Связанные модули и карта
13. type vs interface в TypeScript?
Теги: typescript, type-vs-interface, api-contracts, styleguide
Сложность: Middle
Короткий ответ
Оба описывают форму данных, но interface проще расширять через declaration merging, а type гибче для union/intersection и утилитарных комбинаций.
Что сказать на интервью (30-60 секунд)
interfaceхорошо подходит для описания object shape, public contracts, props and extendable models.typeнужен для union, intersection, mapped/conditional types, tuples и примитивных aliases.- В проекте важнее consistency и styleguide, чем спор "всегда type" или "всегда interface".
- На API boundary типы не валидируют payload в runtime:
ApiDtoдолжен пройти guard/decoder перед превращением в domain model. - Проверяю через DTO/type boundary table:
raw field,interface/type choice,domain shape,runtime check,UI fallback.
Мини-пример
interface User { id: string }
type Status = 'idle' | 'loading' | 'done';
Углубление (2-3 минуты)
- Production-сценарий: backend добавляет
status: 'archived', а UI union знал только'active' | 'blocked'; без boundary handling экран падает или показывает неверный state. - Edge-case: declaration merging у
interfaceможет быть полезен для расширения внешних типов, но опасен, если случайно меняет global shape. - Edge-case: union вида
type Status = 'idle' | 'loading' | 'error'нельзя удобно выразитьinterface. - Практика: выполните TypeScript boundary pack для
UserApiDto -> User -> UserViewState. - Readiness: вы можете объяснить выбор
type/interfaceи отдельно сказать, где нужна runtime validation.
Типичные ошибки
- Делают спор о синтаксисе вместо обсуждения boundary, runtime validation и styleguide.
- Используют
anyна API response и теряют пользу типов дальше по цепочке. - Считают, что
interface UserApiDtoгарантирует форму JSON послеfetch. - Смешивают raw DTO и domain model в одном типе.
Follow-up вопросы
- Когда выбрать
interface, а когдаtype? - Почему TypeScript не проверяет JSON после сети?
- Чем
ApiDtoотличается от domain model? - Где declaration merging полезен, а где опасен?
Что повторить
- TypeScript lesson 1: object shapes, aliases and unions.
- TypeScript boundary pack: DTO/domain/UI mapping.
- Free-practice: boundary choice table for
ApiDto,DomainModel,ViewState.
Связанные модули и карта
14. Как использовать generics в TypeScript на практике?
Теги: typescript, generics, api-client, type-safety
Сложность: Middle
Короткий ответ
Generics позволяют писать переиспользуемые функции/типы с сохранением строгой типизации.
Что сказать на интервью (30-60 секунд)
- Generic нужен, когда функция или тип должны сохранить связь между input и output без потери конкретного типа.
- Хороший generic ограничивает неизвестное через constraints:
T extends { id: string }, а не превращает всё вany. fetchJson<T>()удобен как compile-time helper, но сам по себе не доказывает, что сервер реально вернулT.- Production-риск: слишком широкий generic создает иллюзию безопасности и протаскивает invalid payload в UI.
- Проверяю через generic helper constraints table:
helper,T,constraint,unsafe case,runtime guard.
Мини-пример
function first<T>(arr: T[]): T | undefined {
return arr[0];
}
Углубление (2-3 минуты)
- Production-сценарий: table component принимает
rows: T[]иgetRowId: (row: T) => string, сохраняя тип row в render callback. - Edge-case:
function parse<T>(json): Tбез validation просто утверждает тип и может скрыть ошибку API. - Edge-case: если helper требует
T extends { id: string }, массив безidне скомпилируется. - Практика: опишите
fetchJson<T>(),selectById<T extends { id: string }>(),Table<T>()и укажите, где compile-time заканчивается. - Readiness: вы можете объяснить разницу между inference, explicit generic parameter, constraint and unsafe assertion.
Типичные ошибки
- Используют
<T>как заменуany, хотя связь input/output не нужна. - Пишут
fetchJson<User>()и считают, что это runtime validation. - Не ставят constraints и потом обращаются к полям через casts.
- Делают generic API таким абстрактным, что его сложнее читать, чем конкретный тип.
Follow-up вопросы
- Когда generic лучше overload или конкретного типа?
- Что дает
T extends { id: string }? - Почему
fetchJson<T>()не проверяет runtime shape? - Какой unsafe case сломает слишком широкий generic?
Что повторить
- TypeScript lesson 2: generics and constraints.
- TypeScript boundary pack:
fetchJson<T>()boundary. - Free-practice: generic helper constraints table for API, table and selector helpers.
Связанные модули и карта
15. Что такое discriminated unions и зачем они нужны?
Теги: typescript, discriminated-union, state-modeling, reducers
Сложность: Middle
Короткий ответ
Это union объектов с общим дискриминатором (например, type), который позволяет TypeScript безопасно сужать тип.
Что сказать на интервью (30-60 секунд)
- Discriminated union моделирует взаимоисключающие состояния через общий field:
status,type,kind. - Он убирает impossible states: например,
dataесть только приsuccess, аerrorтолько приerror. - TypeScript сужает тип по discriminator, поэтому в каждом branch доступны только корректные поля.
- Production-риск: boolean flags вроде
isLoading,isError,dataмогут дать противоречивую комбинацию. - Проверяю через async UI state union и exhaustive check:
idle,loading,success,error.
Мини-пример
type State =
| { type: 'idle' }
| { type: 'loading' }
| { type: 'success'; data: User[] }
| { type: 'error'; message: string };
Углубление (2-3 минуты)
- Production-сценарий: экран заказа не должен одновременно показывать loader, старые данные и error banner. Union заставляет выбрать один state.
- Edge-case: при добавлении нового
status: 'empty'exhaustive check вswitchдолжен подсветить все места, где branch не обработан. - Edge-case: discriminator должен быть literal type, а не свободная строка
string. - Практика: сделайте async UI state union для
idle/loading/success/error/emptyи mapping from API result to UI state. - Readiness: вы можете показать, какую impossible комбинацию union запрещает, и где нужен runtime guard для API result.
Типичные ошибки
- Используют несколько boolean flags вместо одного union state.
- Делают discriminator типом
string, из-за чего narrowing теряет силу. - Не добавляют exhaustive check и пропускают новый variant.
- Моделируют raw API response как UI state без mapping layer.
Follow-up вопросы
- Какую impossible state комбинацию запрещает union?
- Что произойдет, если добавить новый variant и не обновить
switch? - Почему discriminator должен быть literal?
- Где нужна runtime validation, если union описывает API payload?
Что повторить
- TypeScript lesson 3: narrowing and discriminated unions.
- TypeScript boundary pack: async UI state union.
- Free-practice: replace
isLoading/isError/dataflags with a discriminated union and exhaustive switch.
Связанные модули и карта
16. Какие utility types вы используете чаще всего?
Теги: typescript, utility-types, api, maintainability
Сложность: Middle
Короткий ответ
Чаще всего Partial, Pick, Omit, Record, Readonly, ReturnType.
Что сказать на интервью (30-60 секунд)
- Utility types помогают получать производные типы из базовой модели без ручного дублирования полей.
PickиOmitподходят для view models и DTO slices;Partial- для patch/update payload;Readonly- для защиты от мутации;Record- для словарей с известными ключами.ReturnTypeиParametersполезны, когда тип должен следовать за функцией, а не жить отдельно и устаревать.- Production-риск:
Partial<User>может случайно разрешить слишком широкий patch, аPickот domain model может протащить поля, которых нет в API. - Проверяю через utility migration table:
source type,target use,utility,forbidden fields,runtime boundary.
Мини-пример
type UserPreview = Pick<User, 'id' | 'name'>;
type UserPatch = Partial<User>;
Углубление (2-3 минуты)
- Production-сценарий: форма редактирования пользователя отправляет только изменяемые поля.
Pick<User, 'name' | 'email'>безопаснее, чемPartial<User>, еслиroleнельзя менять из этой формы. - Edge-case:
Record<string, User>не гарантирует, что ключи реально существуют; для фиксированных ключей лучше union ключей:Record<Role, Permission[]>. - Edge-case:
Readonly<T>защищает compile-time присваивание, но не делает runtime object immutable. - Практика: заполните utility type migration table для
User,UserApiDto,UserPatch,UserPreview,RolePermissions. - Readiness: вы можете объяснить не список utility types, а почему конкретный utility уменьшает дублирование и где он опасен.
Типичные ошибки
- Используют
Partialдля любого update и случайно разрешают менять системные поля. - Дублируют типы вручную, а потом забывают синхронизировать изменения.
- Берут
Pickот domain model для API payload без учета runtime shape. - Думают, что utility type валидирует данные после сети.
Follow-up вопросы
- Когда
Pickбезопаснее, чемPartial? - Почему
Readonly<T>не заменяет runtime immutability? - Чем
Record<Role, Permission[]>лучшеRecord<string, Permission[]>? - Где utility type заканчивается и начинается runtime validation?
Что повторить
- TypeScript lesson 4: utility types and maintainability.
- TypeScript boundary pack: DTO/domain/UI mapping.
- Free-practice: utility type migration table for one form and one API patch.
Связанные модули и карта
17. Promise.all vs Promise.allSettled?
Теги: javascript, promise-all, allsettled, resilience
Сложность: Middle
Короткий ответ
Promise.all падает на первой ошибке, Promise.allSettled дожидается завершения всех промисов и возвращает статус каждого.
Что сказать на интервью (30-60 секунд)
Promise.allподходит, когда результат имеет смысл только если успешны все операции; при первом reject общий promise тоже reject.Promise.allSettledподходит, когда нужны частичные результаты и статус каждой операции: например, несколько независимых виджетов.Promise.allне отменяет остальные promise автоматически; они продолжают выполняться, если их явно не abort/cancel.- Production-риск: одна необязательная ошибка может завалить весь экран, либо наоборот partial failure может быть скрыт как успех.
- Проверяю через promise aggregation matrix:
requests,must all succeed?,partial UI?,failure policy,timeout/cancel.
Мини-пример
const results = await Promise.allSettled([p1, p2, p3]);
Углубление (2-3 минуты)
- Production-сценарий: checkout должен получить price, stock и payment config. Если payment config не загрузился, продолжать нельзя - это
Promise.all. - Production-сценарий: dashboard грузит weather, news, tasks. Один виджет может показать error, остальные остаются полезными - это
Promise.allSettled. - Edge-case: при
Promise.all([fastReject, slowFetch])slow fetch не отменяется сам, поэтому для network нужно добавитьAbortController. - Практика: заполните aggregation matrix для
checkout,dashboard widgets,profile page,bulk upload. - Readiness: вы можете объяснить failure policy и partial state, а не только "all падает, allSettled ждет".
Типичные ошибки
- Используют
Promise.allдля независимых виджетов и получают пустой экран из-за одной ошибки. - Используют
allSettled, но не показывают пользователю partial failure. - Думают, что
Promise.allотменяет остальные запросы. - Не задают timeout/cancel policy для долгих запросов.
Follow-up вопросы
- Что произойдет с остальными promise после первого reject в
Promise.all? - Когда partial result лучше полного fail?
- Как показать пользователю mixed success/mixed failure?
- Где добавить timeout или cancellation?
Что повторить
- JS lesson 2: promise, async order and error handling.
- Interview pattern taxonomy:
execution-order. - Free-practice: promise aggregation matrix for four product scenarios.
Связанные модули и карта
18. Как работает AbortController и зачем он нужен?
Теги: javascript, abortcontroller, cancellation, requests
Сложность: Middle
Короткий ответ
AbortController позволяет отменять fetch и другие abortable операции, чтобы не держать лишние запросы и состояния.
Что сказать на интервью (30-60 секунд)
AbortControllerсоздаетsignal, который передают в abortable API, чаще всегоfetch.controller.abort()переводит signal в aborted state, аfetchобычно завершается reject сAbortError.- Это нужно для cleanup в
useEffect, отмены устаревших search requests, timeout и ухода со страницы. - Abort не откатывает уже выполненное действие на сервере и работает только там, где API поддерживает
signal. - Проверяю через cancellation reproduction: быстрый ввод
a -> ab -> abc, abort старых запросов, запись только актуального результата.
Мини-пример
const c = new AbortController();
fetch('/api', { signal: c.signal });
c.abort();
Углубление (2-3 минуты)
- Production-сценарий: autocomplete отправляет запрос на каждую смену query. Старый медленный ответ не должен перетереть новый.
- Edge-case: abort после того, как сервер уже обработал запрос, не отменяет side effect на backend.
- Edge-case: не все async операции abortable; для них нужен request id, ignore flag или собственная cancellation logic.
- Практика: выполните React effect/state debug pack и отдельно запишите
abort,ignore stale,timeout. - Readiness: вы можете отличить network cancellation от защиты state update на клиенте.
Типичные ошибки
- Создают
AbortController, но не передаютsignalвfetch. - Не отличают abort error от настоящей ошибки сервера.
- Считают, что abort отменяет действие, которое сервер уже выполнил.
- Не делают cleanup в effect и получают stale state update.
Follow-up вопросы
- Что именно происходит при
controller.abort()? - Как отличить
AbortErrorот server error? - Почему abort не всегда заменяет request id?
- Где поставить cleanup в React effect?
Что повторить
- React lesson 2: effect cleanup and cancellation.
- React effect/state debug pack: stale response reproduction.
- Free-practice: cancellation reproduction for autocomplete with timeout.
Связанные модули и карта
19. Что такое делегирование событий и когда оно полезно?
Теги: javascript, event-delegation, dom, performance
Сложность: Middle
Короткий ответ
Это обработка событий на общем родителе вместо подписки на каждый дочерний элемент.
Что сказать на интервью (30-60 секунд)
- Делегирование использует bubbling: один listener на родителе обрабатывает события от дочерних элементов.
- Это полезно для больших или динамических списков, где элементы добавляются/удаляются без пересоздания listener на каждый item.
- Механика обычно строится на
event.target,closest,datasetи проверке, что найденный элемент принадлежит нужному контейнеру. - Production-риск: не все события bubbling-friendly, вложенные элементы могут дать неверный target, а listener может поймать чужой nested markup.
- Проверяю через delegated event checklist:
event bubbles?,selector,container guard,dynamic item,keyboard/a11y path.
Мини-пример
list.addEventListener('click', e => {
const btn = e.target.closest('[data-id]');
if (!btn) return;
});
Углубление (2-3 минуты)
- Production-сценарий: таблица с 5 000 строк имеет кнопки action. Один listener на
tbodyпроще и дешевле, чем listener на каждую кнопку. - Edge-case: клик по иконке внутри кнопки дает target на SVG, поэтому нужен
closest('[data-id]'). - Edge-case:
focusне bubble так же, какclick; нужно знатьfocusin/focusoutили другой подход. - Практика: заполните delegated event checklist для
todo list,table actions,menu,nested card. - Readiness: вы можете объяснить target/currentTarget, bubbling и guard against wrong container.
Типичные ошибки
- Используют
event.targetбезclosestи ломают клики по вложенным элементам. - Не проверяют, что найденный элемент принадлежит текущему контейнеру.
- Делегируют события, которые не всплывают ожидаемым образом.
- Забывают keyboard path и делают interaction только mouse-only.
Follow-up вопросы
- Чем
event.targetотличается отevent.currentTarget? - Зачем нужен
closest? - Какие события плохо подходят для простого delegation?
- Как проверить keyboard/a11y поведение delegated action?
Что повторить
- JS lesson 2: event loop and DOM events.
- Мосты практики:
Browser storage and CORSплюс DOM/a11y audit pack. - Free-practice: delegated event checklist for a dynamic list with nested buttons.
Связанные модули и карта
20. Что такое tree shaking и от чего он зависит?
Теги: frontend, tree-shaking, bundling, performance
Сложность: Middle
Короткий ответ
Tree shaking удаляет неиспользуемые экспорты из бандла, если код и сборка позволяют это статически определить.
Что сказать на интервью (30-60 секунд)
- Tree shaking удаляет неиспользуемые exports, когда bundler может статически понять import/export graph.
- Лучше всего работает с ESM (
import/export), production build, minifier и package metadata вродеsideEffects. - Хуже работает с dynamic require, CommonJS, namespace imports с side effects, barrel files с побочными эффектами и кодом, который выполняется при import.
- Production-риск: разработчик думает, что импортировал одну функцию, а в bundle попал весь package или side-effect module.
- Проверяю через tree-shaking import audit:
import,module format,side effects,bundle evidence,safer import.
Мини-пример
import { usedFn } from './utils.js'; // не импортируем весь namespace без нужды
Углубление (2-3 минуты)
- Production-сценарий: импорт
import _ from 'lodash'может принести лишний код, а точечный ESM import или lightweight helper уменьшает bundle. - Edge-case:
export * from './moduleWithSideEffect'в barrel file может помешать удалению кода, если module выполняет side effect. - Edge-case: tree shaking не обязан удалить код, если package помечен как having side effects или bundler не может доказать безопасность удаления.
- Практика: заполните import audit для
lodash, icon library, date library, internal barrel file, CSS side-effect import. - Readiness: вы можете назвать условия, при которых unused export будет удален, и условия, когда он останется в bundle.
Типичные ошибки
- Думают, что любой named import автоматически гарантирует маленький bundle.
- Не проверяют production build и bundle analyzer/evidence.
- Делают barrel exports с побочными эффектами.
- Путают tree shaking, code splitting и minification.
Follow-up вопросы
- Почему ESM помогает tree shaking?
- Чем tree shaking отличается от code splitting?
- Как
sideEffectsвлияет на удаление модулей? - Как доказать, что импорт реально уменьшил bundle?
Что повторить
- Package Managers and Bundlers: ESM, bundle and dependency risk.
- Interview pattern taxonomy:
performance-budget. - Free-practice: tree-shaking import audit for five imports.
Связанные модули и карта
Куда дальше
- Вернитесь в модуль: React.
- Сверьтесь с Мостами практики: Frontend базовое покрытие и выберите слабую строку.
- Продолжайте по маршруту: Middle трек.
- Закрепите один ответ через named free-practice из этой страницы или в Песочнице.