Сообщества
Frontend
IT
Администраторы
Администраторы скрыты
Создать пост
Frontend

Циклы в JavaScript

Циклы позволяют выполнять код повторно. Знание всех видов циклов и когда их применять - базовый навык фронтендера.

Виды циклов

  • for - известно количество итераций
  • while - условие, количество итераций неизвестно
  • do...while - нужно выполнить тело хотя бы раз
  • for...of - перебор итерируемых объектов (массивы, строки)
  • for...in - перебор ключей объекта
  • forEach - перебор массива без возможности прервать

1. for - самый узнаваемый

for (let i = 0; i < 5; i++) {
  console.log(i); // 0, 1, 2, 3, 4
}

Структура: for (инициализация; условие; шаг)

Пример с массивом:

const fruits = ['яблоко', 'банан', 'вишня'];

for (let i = 0; i < fruits.length; i++) {
  console.log(`${i}: ${fruits[i]}`);
}
// 0: яблоко
// 1: банан
// 2: вишня

2. while - пока условие истинно

let count = 0;

while (count < 3) {
  console.log(count); // 0, 1, 2
  count++;
}

Реальный пример - ожидание данных:

let attempts = 0;
let data = null;

while (!data && attempts < 5) {
  data = fetchData(); // попытка получить данные
  attempts++;
}

3. do…while - выполнить хотя бы раз

let num;

do {
  num = Math.random();
  console.log(num);
} while (num < 0.5); // повторяем, пока число меньше 0.5

Отличие от while: тело выполнится минимум один раз, даже если условие сразу ложно.

4. for…of - перебор значений

Работает с: массивы, строки, Map, Set, NodeList.

const colors = ['red', 'green', 'blue'];

for (const color of colors) {
  console.log(color);
}
// red, green, blue

С индексом через entries():

for (const [index, color] of colors.entries()) {
  console.log(`${index}: ${color}`);
}
// 0: red, 1: green, 2: blue

Перебор строки:

for (const char of 'hello') {
  console.log(char); // h, e, l, l, o
}

5. for…in - перебор ключей объекта

const user = { name: 'Алина', age: 25, city: 'Москва' };

for (const key in user) {
  console.log(`${key}: ${user[key]}`);
}
// name: Алина
// age: 25
// city: Москва

Осторожно: for...in обходит и унаследованные свойства. Используй hasOwnProperty для проверки:

for (const key in user) {
  if (user.hasOwnProperty(key)) {
    console.log(key);
  }
}

6. forEach - метод массива

const nums = [1, 2, 3];

nums.forEach((num, index) => {
  console.log(`[${index}] = ${num}`);
});
// [0] = 1, [1] = 2, [2] = 3

Ограничения forEach:

  • Нельзя прервать через break
  • Не возвращает новый массив (для этого - map)
  • Не работает с async/await как ожидается

Управление циклом: break и continue

// break - выход из цикла
for (let i = 0; i < 10; i++) {
  if (i === 5) break;
  console.log(i); // 0, 1, 2, 3, 4
}

// continue - пропустить итерацию
for (let i = 0; i < 5; i++) {
  if (i === 2) continue;
  console.log(i); // 0, 1, 3, 4
}

Частые ошибки

// Бесконечный цикл - забыли инкремент
while (true) {
  console.log('ops...'); // зависнет браузер
}

// for...in на массиве - плохая практика
const arr = [10, 20, 30];
for (const key in arr) {
  console.log(key); // "0", "1", "2" - строки, не числа!
}

// async в forEach не работает как ожидается
const ids = [1, 2, 3];
ids.forEach(async (id) => {
  const data = await fetch(`/api/${id}`); // не ждет завершения!
});

// Правильно - for...of с async/await
for (const id of ids) {
  const data = await fetch(`/api/${id}`); // работает корректно
}

Шпаргалка: что выбрать

Массив, нужен индекс?          -> for (классика)
Массив, только значения?       -> for...of
Массив, без break/continue?    -> forEach
Объект, ключи?                 -> for...in
Условие, не массив?            -> while
Хотя бы одно выполнение?      -> do...while
Async/await внутри цикла?      -> for...of (НЕ forEach!)
Frontend

Оптимизация SSR приложений: типичные проблемы и решения

Server-Side Rendering открывает отличные возможности для повышения производительности и SEO фронтенд приложений. Однако при внедрении SSR часто возникают узкие места, которые могут замедлить приложение еще больше, чем при клиентском рендеринге. Давайте разберемся, в чем основные проблемы и как их решать.

Главные проблемы при SSR

1. Медленный первый рендер на сервере

Самая частая проблема - это долгий процесс построения HTML на сервере. Когда каждый запрос требует полной сборки дерева компонентов, это может занять сотни миллисекунд, особенно для сложных приложений.

Причины:

  • Обработка больших наборов данных в компонентах
  • Синхронные операции при инициализации
  • Неэффективный поиск данных для каждого рендера

Решение: Используйте кэширование результатов рендера, предварительную загрузку данных и оптимизируйте вычисления. Рассмотрите возможность частичного рендера критичных компонентов.

2. Проблемы с гидрацией

Гидрация - это процесс, когда браузер “оживляет” статический HTML, добавляя слушатели событий и восстанавливая состояние. Несоответствие между HTML на сервере и тем, что генерирует браузер, приводит к ошибкам.

Типичные ошибки гидрации:

  • Условный рендер элементов (if на клиенте отличается от сервера)
  • Использование Date.now() или Math.random()
  • Различия в обработке атрибутов HTML
  • Несинхронизированное состояние при инициализации

Решение: Строго следите за тем, чтобы рендер на сервере и клиенте был идентичным. Избегайте условных блоков, которые зависят от деталей браузера. Используйте флаги для отключения компонентов на сервере, если нужно.

3. Утечки памяти на сервере

При обработке множества одновременных запросов серверная часть может удерживать в памяти объекты состояния, контексты и ссылки на компоненты. Это быстро исчерпает доступную память.

Решение: Убедитесь, что каждый рендер работает с независимым контекстом. Не используйте глобальные переменные для хранения состояния между запросами. После завершения рендера правильно очищайте ресурсы.

4. Проблемы с асинхронными данными

Загрузка данных перед рендером может занять непредсказуемое время. Если у вас много параллельных запросов, это может перегрузить как сервер, так и внешние API.

Риски:

  • Таймауты запросов
  • Каскадные запросы (один компонент ждет, пока загрузится другой)
  • Перегрузка базы данных

Решение: Устанавливайте временные ограничения на загрузку данных. Используйте кэш на сервере (Redis, in-memory хранилище). Реализуйте pattern “Fetch Early, Render Later” для критичных данных.

5. Узкое место CPU на сервере

Node.js работает в одном потоке, поэтому SSR рендер блокирует обработку других запросов. При высокой нагрузке очередь запросов растет, и время отклика увеличивается экспоненциально.

Решение: Используйте worker threads или несколько процессов Node.js за балансировщиком нагрузки. Рассмотрите использование Rust (например, Deno) или других более быстрых сред для критичных частей. Установите лимиты на время рендера с fallback на клиентский рендер.

6. Синхронизация состояния между сервером и клиентом

Если состояние, которое вы подготовили на сервере, не совпадает с тем, что браузер получит от API, произойдут проблемы с гидрацией или функциональностью.

Решение: Сериализуйте начальное состояние в JSON и встройте его в HTML. Клиент должен использовать это состояние при инициализации. Используйте предсказуемые механизмы для воспроизведения данных.

7. Проблемы с внешними зависимостями

Библиотеки, написанные для браузера, могут работать неправильно на сервере. Глобальные объекты, DOM API, window объект не доступны в Node.js окружении.

Типичные проблемы:

  • window is not defined
  • document is not defined
  • localStorage не существует

Решение: Оборачивайте код, зависящий от браузера, в условные блоки. Используйте динамический импорт для загрузки браузер-специфичного кода только на клиенте. Проверяйте наличие глобальных объектов перед их использованием.

Практические советы по оптимизации

Измеряйте время рендера

Используйте профилировщик для выявления узких мест:

const start = performance.now();
const html = renderToString(app);
const duration = performance.now() - start;
console.log(`Render time: ${duration}ms`);

Кэшируйте результаты

Сохраняйте отрендеренный HTML для часто запрашиваемых URL. Используйте ETags для проверки актуальности кэша.

Streamable HTML

Вместо ожидания полного рендера, начните отправлять HTML по частям с использованием streaming. Это улучшит perceived performance для пользователя.

Предварительная генерация страниц

Для статичного контента используйте Static Site Generation (SSG) вместо рендера на каждый запрос. Это кардинально ускорит приложение.

Оптимизируйте компоненты

Убедитесь, что компоненты не выполняют ненужных вычислений. Избегайте отрендеринга одних и тех же элементов несколько раз.

// Плохо
const HeavyComponent = () => {
  const data = expensiveCalculation();
  return <div>{data}</div>;
};

// Хорошо
const HeavyComponent = ({ data }) => {
  return <div>{data}</div>;
};

Заключение

SSR может дать мощный прирост производительности и улучшить SEO, но требует тщательного подхода. Основные ключи к успеху:

  • Постоянное профилирование и мониторинг
  • Изоляция состояния между запросами
  • Правильная обработка асинхронных операций
  • Кэширование и streaming
  • Тестирование гидрации на реальных условиях

Не пренебрегайте мониторингом - используйте инструменты для отслеживания времени рендера и памяти. Помните, что иногда лучше пожертвовать некоторыми требованиями SSR в пользу производительности.

Frontend

SPA Routing

SPA Routing - это когда навигация по приложению происходит полностью внутри браузера, без запросов на сервер за новой HTML-страницей при каждом клике.

Как это работает

  1. Сервер при первом запросе отдаёт один-единственный index.html
    В нём - <div id="root"></div> и ссылка на огромный js-бандл

  2. JavaScript загружается и сразу читает текущий адрес:
    window.location.pathname > /profile/settings

  3. На основании пути роутер решает, какой компонент показать(чаще всего это дерево маршрутов)

  4. При клике по ссылке происходит примерно следующее:

function handleClick(e) {
  e.preventDefault();           // ← самое важное
  const path = e.currentTarget.getAttribute("href");
  window.history.pushState({}, "", path);  // меняем URL
  // дальше либо setState, либо подписка на popstate / событие роутера
  // → приложение перерисовывает нужный кусок
}
  1. Браузерная кнопка «назад» вызывает событие popstate > роутер снова читает location.pathname и перерисовывает экран

Именно поэтому в хорошем SPA при нажатии «назад» всё выглядит плавно и состояние восстанавливается.

Что использовать для настройки?

  • React Router - классика для реакт приложений
  • Next.js App Router - когда нужен и SSR, и клиентский роутинг, и file-based routing
  • Vue Router, SvelteKit routing, SolidStart, Astro + view transitions - для каждого фреймворка свой раутер

С какими проблемами можно столкнуться?

  1. Сервер не знает про клиентские маршруты
    > заходишь по прямой ссылке /users/948302 > 404
    Решение: на сервере (nginx / express / vercel / netlify) настроить, чтобы любой нестатический путь возвращал 404page

  2. Потеря состояния при рефреше
    > был открыт аккордеон, выбран фильтр, прокручена страница > F5 > всё слетело
    Решение: сохранять в URL (search params), localStorage, IndexedDB или в глобальном состоянии с персистенсом

  3. Дублирующиеся запросы при «назад» / «вперёд»
    > нажал назад > опять полетел запрос за теми же данными
    Решение: кэширование (React Query, SWR, TanStack Query, next/cache)

  4. Scroll restoration ломается
    > после возврата назад страница не прокручивается туда, где была
    Решение: вручную сохранять scroll position или использовать window.history.scrollRestoration = "manual"

  5. Бесконечные редиректы / зацикливание
    > ProtectedRoute > Login > back to protected > redirect loop
    Часто из-за неправильной проверки isAuthenticated в сочетании с useEffect

Безопасность

Клиентский роутинг не даёт никакой защиты - это чисто UX-механизм. Всё, что происходит на фронте, пользователь может изменить.

Самые опасные ловушки:

  • Фейковые route guards на клиенте
   if (localStorage.getItem("role") !== "admin") {
    navigate("/no-access");
  }
    // любой в devtools меняет localStorage и заходит куда угодно  

Решение: Используйте «безопасные данные» или проверки на беке. Проверка должна производиться только с данными из стейта, которые нельзя поменять

  • Открытые данные в бандле
    Весь код SPA скачивается пользователю. Часто в нём лежит:
    • структура всех роутов (включая админские)
    • endpoint’ы API
    • иногда даже чувствительная логика или тестовые токены😁

Решение: code splitting + server-side role-based chunk loading (очень редко реализуют)

  • Манипуляция URL и history API
    Злоумышленник может подменить pathname вручную > если данные грузятся по пути без проверки на бэке > утечка данных

Routing это не просто смена компонентов по путям, но также:

  • синхронизации URL и стейта
  • предзагрузка данных
  • управление скроллом
  • обработка ошибок и fallback’ов
  • кэширование
  • красивые переходы
  • валидации переходов

Чтож, если есть какие-то интересные кейсы, то пишите в комменты 💬

Frontend

Interface vs Type: что выбрать?

При работе с TypeScript, вы наверняка задавались вопросом: «Что использовать - interface или type?». Кажется, что они делают одно и то же, но под капотом скрываются нюансы, которые могут либо спасти ваш проект, либо докинуть задачек в беклог

Interface: Контракт для объектов

interface - это, прежде всего, описание структуры объекта. Это классический способ определения формы данных в ООП-стиле.
Главная фишка: Declaration Merging

Интерфейсы позволяют «дописывать» себя. Если вы объявите один и тот же интерфейс дважды, TypeScript автоматически их объединит.

interface User {
  name: string;
}

interface User {
  age: number;
}

// Итог: User теперь имеет и name, и age
const person: User = { name: "Alex", age: 25 };

Зачем это нужно?

Это идеально для расширения сторонних библиотек или глобальных объектов (например, добавление поля в window).

Type: более «гибкая» типизация

type - это более гибкий инструмент для типизации.

Он может быть не только объектом, но и псевдонимом для любого типа данных.

Главная фишка: Union и Intersection

Типы умеют то, что интерфейсам недоступно - логические операции.

type Status = "success" | "error" | "loading"; // Union тип
type ID = string | number;

type Point = { x: number };
type Dimension = { width: number };

// Пересечение (Intersection)
type Rect = Point & Dimension;

Что и когда выбирать?

  • Используйте interface, когда:
    • Вы описываете структуру объекта, который может расширяться.
    • Вы пишете публичную библиотеку (чтобы пользователи могли мержить интерфейсы).
  • Используйте type, когда:
    • Нужны Union-типы (например, status: 'open' | 'closed').
    • Вы работаете с кортежами (Tuples).
    • Нужно создать алиас для примитива или сложной функции.

Исходя из своего опыта, могу сказать, что:

  • interface чаще используется для типизации объектов(type используется редко)

  • type чаще используется для примитивных типов, либо, в редких случая для приватноц типизации объектов в локальных местах для удобства (например:Type1 | Type2 или Type1 & Type2)

2
Frontend

CSS Препроцессоры. В чем суть?🤔

CSS - это каскадная таблица стилей, он статичный и ограниченный. Препроцессор (например, Sass, Less или Stylus) - это инструмент, который позволяет писать "расширенный" CSS с дополнительными фичами. Он компилируется в обычный CSS, который понимает браузер.

В итоге, вы пишете код быстрее и чище. Это не замена CSS, а хелпер для вас, чтобы вам было удобнее писать/читать стили

Какие проблемы решает?

Чистый CSS имеет кучу болячек, особенно в больших проектах. Препроцессоры их фиксят:

  • Вложенность и организация: CSS не поддерживает вложенные селекторы нативно (хотя в новых версиях есть, но не везде). Препроцессоры позволяют писать стили как дерево, что делает код читаемым.
  • Математика и функции: Хотите рассчитать ширину на лету? В CSS это геморрой, а здесь - легко с операторами и функциями.
  • Миксины и наследование: Переиспользуйте куски стилей, как функции в программировании.
  • Поддержка кросс-браузерности: Автоматически добавляют префиксы (например, -webkit-).

В общем, препроцессоры превращают CSS из "спагетти" в элегантный код, экономя время и нервы.

Почему нужно использовать?

Потому что в 2к26 году веб-разработка - это огромная индустрия в которую часто сложно вкатиться, и понять в ней многие вещи. Так зачем их усложнять?

  • Упрощение: Пишешь меньше, но эффективнее.
  • Улучшаете поддерживаемость: Команда легко понимает и редактирует код.
  • Экономите на ошибках: Легче отлавливать баги в структурированном коде.

Наглядные примеры

Давайте сравним чистый CSS и Sass (самый популярный препроцессор). Установите Sass через npm, и компилируйте: sass input.scss output.css.

Пример 1: Вложенность (решает организацию)

Чистый CSS:

.nav ul {
  list-style: none;
}

.nav ul li {
  display: inline-block;
}

.nav ul li a {
  text-decoration: none;
  color: #000;
}

.nav ul li a:hover {
  color: #007bff;
}

Повторяющиеся селекторы - утомительно.

Sass (с вложенностью):

.nav {
  ul {
    list-style: none;
    
    li {
      display: inline-block;
      
      a {
        text-decoration: none;
        color: #000;
        
        &:hover {
          color: #007bff;
        }
      }
    }
  }
}

Как HTML-структура! Читаемо и логично.

Пример 2: Миксины (решает переиспользование)

Чистый CSS: Нет аналога, копируй стили.

Sass (миксин для кнопки):

@mixin button-style($bg-color, $text-color) {
  background-color: $bg-color;```
  color: $text-color;
  padding: 10px 20px;
  border-radius: 5px;
}

.primary-btn {
  @include button-style(#007bff, #ffffff);
}

.secondary-btn {
  @include button-style(#6c757d, #ffffff);
}

Создал шаблон - используй везде. Идеально для тем и вариаций!

Да и в целом, в биг-техе это зачастую будет огромным плюсом при найме, а зачастую, даже требованием для устройства в компанию

Frontend

В чем сила SSR? 🤔

Давайте разберёмся, зачем он нужен, какие проблемы решает и почему без него в наше время уже не обойтись. Я приведу простые примеры, чтобы всё было более наглядно

Что такое SSR и зачем он нужен?

SSR - это подход, при котором HTML-страница генерируется на сервере перед отправкой клиенту. В отличие от Client-Side Rendering (CSR), где браузер сам собирает страницу из JavaScript, SSR делает всю тяжёлую работу на серверной части приложения.

Зачем это нужно? В эпоху, когда пользователи ожидают мгновенной загрузки (например, TikTok и Instagram), SSR помогает создать быстрый, SEO-дружественный и доступный веб. Он идеален для динамических сайтов, где контент меняется (например, блоги, e-commerce или соцсети).

Какие проблемы решает SSR?

  1. Медленная первоначальная загрузка (FCP).
    В CSR пользователь видит белый экран, пока JS не загрузится и не отрендерит контент. SSR отправляет уже готовый HTML - и страница отображается сразу
    Пример: На мобильном с слабым интернетом CSR-сайт может грузиться 5–10 секунд. SSR сокращает это до 1–2 секунд, снижая отток пользователей на 20–30% (по данным Google).

  2. Проблемы с SEO.
    Поисковики лучше индексируют статический HTML. В CSR боты видят пустой шаблон и уходят ни с чем. SSR даёт им полный контент.
    Пример: Блог на чистом React (CSR) может не появляться в топе поиска. Перейдите на Next.js с SSR и ваш пост будет отображаться выше в поиске

  3. Производительность на слабых устройствах.
    Не все пользователи используют новейшее железо - многие до сих пор юзают очень старые девайсы. CSR нагружает клиентский CPU, вызывая лаги. SSR перекладывает рендеринг на мощный сервер.
    Пример: Онлайн-магазин с тысячами товаров. В CSR браузер тормозит при скролле. SSR рендерит список на сервере - и всё "летает" даже на старом устройстве.

Почему стоит использовать SSR?

Веб эволюционирует: Core Web Vitals от Google карают медленные сайты, также как и пользователи, которые могут просто скипнуть ваш сайт, если он будет грузится довольно долго. SSR - это must-have для:

  • Конкурентного преимущества: Сайты на SSR (Next.js, Nuxt.js, Remix) загружаются быстрее, что повышает конверсию на 7–12% (исследования Amazon).
  • Гибкости: Можно комбинировать с CSR (Hybrid Rendering) для интерактивных частей.
  • Масштабируемости: Легко кэшировать страницы, снижая нагрузку на сервер.

Если вы строите что-то серьёзное — от личного блога до корпоративного портала — внедряйте SSR. Начните с фреймворков вроде Next.js, и почувствуйте разницу:)

Frontend

Зачем использовать TypeScript?

Представьте, вы пишете большой фронтенд-проект на JavaScript. Всё работает локально, тесты зелёные, вы деплойите в продакшн... и внезапно приложение падает с ошибкой Cannot read property 'map' of undefined…динамическая типизация…мммм…

Вот тут бы вас выручил TypeScript - надстройка над JavaScript, которая добавляет статическую типизацию и ловит кучу ошибок ещё до запуска кода.

Какую задачу решает TypeScript?

JavaScript — динамически типизированный язык. Это создаёт много дичи при разработке: ошибки типов проявляются только во время выполнения. TypeScript решает эту проблему, проверяя типы на этапе компиляции. Вы получаете:

  • Раннее обнаружение ошибок на этапе разработки
  • Лучшую автодополняемость и навигацию в IDE
  • Самодокументирующийся код
  • Упрощённый рефакторинг больших проектов

Главные плюсы TypeScript

  1. Ловит баги до продакшна — самые дорогие ошибки обнаруживаются на этапе разработки или при создании мердж реквеста
  2. Отличная поддержка в редакторах — VS Code подсвечивает ошибки мгновенно, автодополнение знает всё о ваших типах.
  3. Масштабируемость — в проектах на 10k+ строк без типов легко потеряться. TS делает код предсказуемым.
  4. Постепенное внедрение — можно добавлять TS в существующий JS-проект по файлу за файлом (расширение .ts или .tsx).
  5. Поддержка современного JS — все фичи ESNext + свои собственные (enums, tuples, generics и др.).
  6. Используется везде — Angular, React (с типами), Vue 3, NestJS, Deno и даже многие Node.js-проекты.

Где TypeScript упрощает жизнь

Пример 1: Ошибка с undefined

// JavaScript — работает, пока не придут кривые данные 
function renderUsers(users) {
  return users.map(user =&gt; `&lt;div&gt;${user.name}&lt;/div&gt;`).join('');
}

renderUsers(null); // 💥 TypeError: Cannot read property 'map' of null
// TypeScript — ошибка на этапе компиляции
function renderUsers(users: Array&lt;{ name: string }&gt; | null) {
  if (!users) return 'Нет пользователей';
  return users.map(user =&gt; `&lt;div&gt;${user.name}&lt;/div&gt;`).join('');
}

renderUsers(null); // OK, обработано
renderUsers(undefined); // ❌ Ошибка компиляции: Argument of type 'undefined' is not assignable to parameter of type 'Array&lt;{ name: string }&gt; | null'

Пример 2: Неправильные свойства объекта

// JS— молча вернёт undefined
function getUserEmail(user) {
  return user.email.toLowerCase();
}

getUserEmail({ name: 'Влад', gmail: 'vlad@example.com' }); // 💥 Cannot read property 'toLowerCase' of undefined
// TS — сразу ругается
interface User {
  name: string;
  email: string;
}

function getUserEmail(user: User): string {
  return user.email.toLowerCase();
}

getUserEmail({ name: 'Влад', gmail: 'vlad@example.com' });
// ❌ Property 'email' is missing in type '{ name: string; gmail: string; }' but required in type 'User'

Ловит опечатки и неправильную структуру данных

Пример 3: Рефакторинг без страха

Допустим, вы переименовали поле userId → id. В чистом JS нужно искать все использования вручную. В TypeScript:

interface User {
  id: number; // было userId
}

// Все места, где использовался user.userId, теперь подсвечены красным
user.userId; // ❌ Property 'userId' does not exist on type 'User'

Мгновенно видите все проблемные места.

Если вы до сих пор используете чистый js в огромных проектах, то я вам не завидую, заканчивайте, и будьте счастливы 😁🙌