
- Главная
- Каталог
- Интернет технологии
- Frontend разработчик
Frontend разработчик
Библиотека Frontend программиста. Книги, полезные материалы, статьи, новости, уроки по front end/web разработке.
Статистика канала
.join(', ')
Каждый фронтендер хотя бы раз писал функцию, которая превращает массив ['A', 'B', 'C'] в строку «A, B и C».
Обычно это выглядит как костыль:
// ❌ Старый, хрупкий способ
const names = ['Alex', 'John', 'Sam'];
const str = names.slice(0, -1).join(', ') + ' и ' + names.at(-1);
// А если массив пустой? А если один элемент? Нужно писать кучу if-ов.
{}
В JavaScript уже давно есть встроенный API для этого - Intl.ListFormat. Он делает то же самое, но правильно и с учетом языковых правил (локали).
✅ Как это делается профессионально:
const tools = ['React', 'Vue', 'Svelte'];
// 1. Создаем форматтер для русского языка
const ruFormatter = new Intl.ListFormat('ru', {
style: 'long', // 'long', 'short', 'narrow'
type: 'conjunction' // 'conjunction' (и), 'disjunction' (или), 'unit' (просто список)
});
console.log(ruFormatter.format(tools));
// "React, Vue и Svelte"
// 2. Меняем локаль на английскую — логика "Oxford comma" работает сама!
const enFormatter = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' });
console.log(enFormatter.format(tools));
// "React, Vue, and Svelte"
{}
🔥 Вариант с «ИЛИ» (disjunction): Идеально для сообщений об ошибках или подсказок.
const orFormatter = new Intl.ListFormat('ru', { type: 'disjunction' });
console.log(orFormatter.format(['строка', 'число']));
// "строка или число"
{}
Почему это круто:
1. Ноль зависимостей. Не нужно тащить i18n-библиотеки ради списков.
2. Умная грамматика. API знает, где ставить запятые, где союзы, и как это делать в китайском, арабском или немецком языках.
3. Обработка края. Корректно работает с пустыми массивами и массивами из одного элемента без лишних проверок.
📲 Мы в MAX
👉 @frontend_1pointer-events: none для блокировки UI
Когда вы открываете Side-menu (шторку), показываете лоадер или переключаете шаги в форме, часто нужно «заморозить» взаимодействие с определенной частью страницы.
🐢 Старый (плохой) способ:
Вешаем CSS-класс .disabled:
.disabled {
opacity: 0.5;
pointer-events: none; /* Блокирует клики */
user-select: none;
}
{}
Почему это плохо? Это визуальный обман.
1. Пользователь всё еще может попасть внутрь фокусом через Tab.
2. Пользователь может нажать Enter на кнопке, которую «нельзя нажать».
3. Скринридеры (VoiceOver, NVDA) продолжают читать этот контент, путая незрячих пользователей.
🚀 Правильный способ: Атрибут inert
Это глобальный HTML-атрибут, который делает элемент и всех его детей полностью неактивными для браузера.
<main id="main-content" inert>
<button>Я не нажимаюсь</button>
<input placeholder="В меня нельзя писать" />
</main>
<aside id="sidebar">
<button>Я работаю!</button>
</aside>
{}
Что делает inert под капотом:
1. pointer-events: none: Клики и ховеры игнорируются.
2. tabindex="-1": Элемент и все его потомки исключаются из Tab-навигации.
3. aria-hidden="true": Дерево доступности (A11y Tree) игнорирует этот узел (скринридеры молчат).
4. Запрет выделения: Текст нельзя выделить мышкой.
Как переключать в JS:
Это обычное свойство DOM-элемента, как disabled или hidden.
const main = document.getElementById('main-content');
const loaderVisible = true;
// Одной строкой выключаем всё взаимодействие с main
main.inert = loaderVisible;
{}
📲 Мы в MAX
👉 @frontend_1@container
Мы привыкли делать адаптив через @media, опираясь на размер экрана. Но это ломает компонентный подход.
Представьте карточку товара. На десктопе в основной сетке она широкая, а в сайдбаре - узкая. Если вы используете @media (min-width: 1200px), то карточка в сайдбаре будет "думать", что места много, и развалится, хотя сам сайдбар узкий.
Раньше мы лечили это пропсами isSmall, variant="sidebar" или сложным CSS. Теперь есть нативное решение Container Queries.
В чем суть?
Компонент теперь смотрит не на ширину окна браузера (viewport), а на ширину своего родительского контейнера.
Как использовать:
1. Обозначаем родителя как контейнер
Чтобы дочерние элементы могли "измерять" родителя, нужно явно включить этот режим.
.card-wrapper {
/* Включаем отслеживание изменения размера по горизонтали */
container-type: inline-size;
/* Опционально: даем имя, чтобы обращаться к конкретному контейнеру */
container-name: card-box;
}
{}
2. Пишем стили для ребенка
Вместо @media используем @container.
.card {
display: flex;
flex-direction: column;
}
/* Если РОДИТЕЛЬ (.card-wrapper) шире 400px */
@container card-box (min-width: 400px) {
.card {
flex-direction: row; /* Перестраиваем в горизонталь */
gap: 20px;
}
}
{}
Вы создаете по-настоящему изолированный компонент.
Вы можете кинуть этот компонент в футер, в модалку, в грид на 3 колонки или на весь экран, он сам подстроится под выделенное ему место. Вам больше не нужно знать контекст, где компонент будет использоваться.
📲 Мы в MAX
👉 @frontend_1Меня зовут Тихон, привет! Я — действующий Frontend-разработчик и ментор. Помогаю устроиться на хорошие позиции в Big Tech и сопровождаю на испытательном сроке.В своем канале: 👉Разбираю самые популярные и каверзные вопросы на собесах 👉Рассказываю как пройти фильтр HR 👉Борюсь с убеждениями, которые мешают развиваться 👉Делюсь лайфхаками, например как аккуратно “пинговать” рекрутеров Регулярно публикую полезные материалы: ▪️60 вопросов, которые точно помогут тебе на собеседовании. ▪️Подборка из 100+ каналов с вакансиями для разработчиков ▪️10 задротских вопросов про JavaScript, после которых ты усомнишься, что вообще знаешь JS. Часть 1 ▪️Чек лист проверки своего резюме Подписывайся, нас уже 4500 🤓: ссылка Реклама, erid2W5zFK72PEp: ИП Галактионов Тихон Витальевич, ИНН 771618975809
Object.groupBy
Сколько раз в жизни вы писали reduce для группировки массива объектов по какому-то полю? Или тянули для этого жирный lodash?
Задача: Сгруппировать продукты по категории.
🐢 Как мы делали раньше (Reduce):
const inventory = [
{ name: "Asparagus", type: "vegetables" },
{ name: "Bananas", type: "fruit" },
{ name: "Goat", type: "meat" },
{ name: "Cherries", type: "fruit" },
];
// Читать сложно, легко ошибиться в мутации аккумулятора
const result = inventory.reduce((acc, item) => {
(acc[item.type] ||= []).push(item);
return acc;
}, {});
{}
🚀 Как это делается теперь:
const result = Object.groupBy(inventory, ({ type }) => type);
/* Результат:
{
vegetables: [{ name: "Asparagus", ... }],
fruit: [{ name: "Bananas", ... }, { name: "Cherries", ... }],
meat: [{ name: "Goat", ... }]
}
*/
{}
🧠 Senior-нюанс: Map.groupBy
Обычный Object.groupBy возвращает объект, где ключи всегда приводятся к строкам.
Если вам нужно сгруппировать данные, где ключом выступает объект (например, пользователь или конфиг), используйте Map.groupBy.
const restockOptions = { threshold: 10 };
const urgentOptions = { threshold: 0 };
// Группируем элементы по объектам-конфигам
const result = Map.groupBy(inventory, (item) => {
return item.quantity < 5 ? urgentOptions : restockOptions;
});
// Получаем доступ по ссылке на объект!
result.get(urgentOptions);
{}
Итог: Еще одна причина перестать тащить utility-библиотеки в бандл.
📲 Мы в MAX
👉 @frontend_1JSON.parse(JSON.stringify())
Глубокое копирование объектов (Deep Copy) долго было болью в JS. Чтобы не копировать ссылки, мы либо тянули жирный lodash.cloneDeep, либо использовали популярный хак с JSON.
Проблема хака JSON.parse(JSON.stringify(obj)) в том, что он теряет данные:
1. Date превращается в строку.
2. Set и Map превращаются в пустые объекты {}.
3. undefined просто исчезает.
4. Циклические ссылки вызывают ошибку.
Теперь у нас есть нативный стандарт - structuredClone().
Как это работает:
const original = {
title: "Dev Meeting",
date: new Date(),
members: new Set(['Alex', 'Sam']),
meta: undefined
};
// ❌ Старый хак
const jsonCopy = JSON.parse(JSON.stringify(original));
console.log(jsonCopy.date); // Строка "2023-10-...", а не объект Date
console.log(jsonCopy.members); // {}, данные потеряны
console.log(jsonCopy.meta); // Ключ исчез совсем
// ✅ structuredClone
const realCopy = structuredClone(original);
console.log(realCopy.date.getFullYear()); // Работает! Это всё ещё Date
console.log(realCopy.members.has('Alex')); // Работает! Это всё ещё Set
console.log(realCopy.meta); // undefined (на месте)
{}
⚠️ Важные ограничения:
structuredClone предназначен для данных.
Он выбросит ошибку DataCloneError, если вы попытаетесь скопировать:
• Функции (методы объекта);
• DOM-элементы;
• Свойства прототипа (копируются только собственные свойства).
📲 Мы в MAX
👉 @frontend_1z-index: 9999: Нативный <dialog>
Мы годами писали сложные велосипеды для модальных окон или тянули тяжелые библиотеки. Главная боль кастомных решений - это accessibility (focus trap), закрытие по Esc и вечные войны с контекстом наложения (z-index).
HTML-элемент <dialog> теперь стабилен во всех браузерах и решает эти проблемы на уровне движка.
Почему это Game Changer:
1. Top Layer API: При открытии через .showModal(), элемент помещается в специальный «верхний слой» браузера. Ему плевать на overflow: hidden родителя или низкий z-index контейнера. Он всегда будет поверх всего.
2. Встроенная доступность: Браузер сам запирает фокус внутри окна (focus trap) и обрабатывает нажатие Esc.
3. Магия форм: Форма с method="dialog" закрывает модалку при сабмите без единой строчки JS-обработчика.
Пример (Минимум кода):
<dialog id="confirmModal">
<form method="dialog">
<h3>Удалить продакшн?</h3>
<p>Это действие необратимо.</p>
<button value="cancel">Отмена</button>
<button value="confirm" autofocus>Да, удалить</button>
</form>
</dialog>
{}
const modal = document.getElementById('confirmModal');
// 1. Открываем (добавляет backdrop и блокирует остальную страницу)
modal.showModal();
// 2. Слушаем закрытие
modal.addEventListener('close', () => {
if (modal.returnValue === 'confirm') {
runDeleteScript();
}
});
{}
🎨 Как стилизовать фон?
Забудьте про создание отдельного div для затемнения. Используйте псевдоэлемент:
dialog::backdrop {
background: rgb(0 0 0 / 0.5);
backdrop-filter: blur(4px); /* Красивое размытие фона */
}
{}
⚠️ Важный нюанс:
Не путайте методы.
▪️ .show() - просто показывает элемент (позционирование absolute, страница скроллится).
▪️ .showModal() - то, что вам нужно (Top Layer, fixed, блокировка фона).
📲 Мы в MAX
👉 @frontend_1isMounted в useEffect
Вы наверняка видели (или писали) такой код, чтобы избежать ошибки "Can't perform a React state update on an unmounted component":
// ❌ Антипаттерн
useEffect(() => {
let isMounted = true;
fetchData().then(data => {
if (isMounted) setState(data);
});
return () => { isMounted = false; };
}, []);
{}
Это «мусорный» код. Запрос все равно происходит, трафик тратится, промис висит в памяти.
Вместо ручных флагов используйте стандартный браузерный API - AbortController.
✅ Как сделать правильно:
useEffect(() => {
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.then(res => res.json())
.then(setData)
.catch(err => {
// Важно: не считаем отмену запроса ошибкой
if (err.name !== 'AbortError') {
console.error(err);
}
});
// При размонтировании или перезапуске эффекта запрос реально отменится браузером
return () => controller.abort();
}, []);
{}
🚀 Бонус-фича: Очистка Event Listeners
AbortController умеет удалять и слушатели событий. Больше не нужно выносить функцию-хендлер в отдельную переменную, чтобы передать её в removeEventListener.
const controller = new AbortController();
// Передаем signal в опции
window.addEventListener('resize', (e) => handleResize(e), {
signal: controller.signal
});
// Одним вызовом удаляем слушатель (или группу слушателей с одним сигналом)
controller.abort();
{}
📲 Мы в MAX
👉 @frontend_1any - это ложь, которую вы говорите сами себе
Давайте честно: когда у вас горят дедлайны, а TypeScript ругается на несовпадение типов, рука сама тянется написать : any.
«Я потом поправлю», - говорите вы.
Спойлер: не поправите.
Использование any превращает ваш строгий TypeScript проект обратно в анархичный JavaScript, но с лишним этапом сборки. Вы просто отключаете компилятор.
🔥 Почему это плохо:
Вы теряете автокомплит, рефакторинг становится русской рулеткой, а баг undefined is not a function вернется к вам в самый неподходящий момент.
🛡 Что делать вместо any?
1. Если вы реально не знаете, что прилетит:
Используйте unknown. Это безопасный аналог any. Он скажет: "Я не знаю, что это, но я не дам тебе с этим работать, пока ты не проверишь тип".
// ❌ Плохо
const processData = (data: any) => {
data.toUpperCase(); // Может упасть, если data — число
}
// ✅ Хорошо
const processData = (data: unknown) => {
if (typeof data === 'string') {
data.toUpperCase(); // TS теперь знает, что это строка
}
}
{}
2. Если лень описывать огромный ответ бэкенда:
Используйте утилиты для генерации типов из Swagger/OpenAPI. Не пишите интерфейсы руками, мы же не в каменном веке.
Уважайте свои нервы. Типизируйте нормально.
#typescript #cleancode #safety
📲 Мы в MAX
👉 @frontend_1Отзывы канала
всего 4 отзыва
- Добавлен: Сначала новые
- Добавлен: Сначала старые
- Оценка: По убыванию
- Оценка: По возрастанию
Каталог Телеграм-каналов для нативных размещений
Frontend разработчик — это Telegam канал в категории «Интернет технологии», который предлагает эффективные форматы для размещения рекламных постов в Телеграмме. Количество подписчиков канала в 11.0K и качественный контент помогают брендам привлекать внимание аудитории и увеличивать охват. Рейтинг канала составляет 5.3, количество отзывов – 4, со средней оценкой 5.0.
Вы можете запустить рекламную кампанию через сервис Telega.in, выбрав удобный формат размещения. Платформа обеспечивает прозрачные условия сотрудничества и предоставляет детальную аналитику. Стоимость размещения составляет 6363.63 ₽, а за 47 выполненных заявок канал зарекомендовал себя как надежный партнер для рекламы в TG. Размещайте интеграции уже сегодня и привлекайте новых клиентов вместе с Telega.in!
Вы снова сможете добавить каналы в корзину из каталога
Комментарий