Основные элементы экосистемы JavaScript по состоянию на 2026 год
- пятница, 10 апреля 2026 г. в 00:00:07
Ранее мы писали похожие статьи о CSS, но JavaScript заслуживает не меньшего внимания! Тем более что JavaScript лучше справляется с версионированием. Мы рассмотрим новые возможности самого языка, а также основные среды выполнения, фреймворки, библиотеки и инструменты.
В JavaScript выпускаются ежегодные версии, и, на мой взгляд, это довольно удобно.
Последняя версия языка — ECMAScript 2025, вышедшая в июне 2025 года (полная спецификация этой версии).
Теперь у нас есть такие методы, как .map(), .filter(), .take(), .drop(), прямо на итераторах, с ленивой оценкой. Честно говоря, для меня, в основном фронтенд-разработчика, это кажется немного эзотерическим. Мы ведь уже можем использовать map для массивов, так в чем проблема? Но я понимаю важность производительности, и это один из аспектов.
const result = array .map(x => x * 2) // создает новый массив в памяти .filter(x => x > 10) // ... и снова .slice(0, 3); // ... и снова
Это “медленный” и “требующий много памяти” способ, особенно если массив очень большой, а выполняемые операции “дорогостоящие”, как говорится. Новый, более современный способ выглядит так:
const result = Iterator.from(array) .map(x => x * 2) .filter(x => x > 10) .take(3) .toArray(); // новых массивов не создается, вычисление останавливается после 3
И в качестве приятного бонуса, Iterator.from() работает с любыми итерируемыми объектами. Т.е. не только с массивами, но и с множествами, отображениями (maps), генераторами и т.д., а это значит, что для всех них доступен один и тот же удобный набор функций.
Множества (sets) в JavaScript довольно удобны, поскольку они похожи на массивы, только каждый элемент в них гарантированно уникален. В этом нет ничего нового, но если у нас есть два множества, теперь есть методы для получения интересной информации о них, например, пересекается они или нет и т.д.
const youKnow = new Set(["JS", "Python", "CSS", "SQL"]); const jobNeeds = new Set(["JS", "TypeScript", "Python"]); // Навыки, необходимые для данной работы, которыми вы уже обладаете youKnow.intersection(jobNeeds); // → Set {"JS", "Python"} // Навыки, которыми вы обладаете + навыки для работы youKnow.union(jobNeeds); // → Set {"JS", "Python", "CSS", "SQL", "TypeScript"} // Навыки для работы, которыми вы пока не обладаете jobNeeds.difference(youKnow); // → Set {"TypeScript"} // Ваши навыки, которые неважны для работы youKnow.difference(jobNeeds); // → Set {"CSS", "SQL"} // Навыки, присутствующие в одном множестве и отсутствующие в другом youKnow.symmetricDifference(jobNeeds); // → Set {"CSS", "SQL", "TypeScript"} // Навыки для работы являются подмножеством ваших навыков? jobNeeds.isSubsetOf(youKnow); // → false // Ваши навыки являются надмножеством навыков для работы? youKnow.isSupersetOf(jobNeeds); // → false // Ваши навыки и навыки для работы не пересекаются? youKnow.isDisjointFrom(jobNeeds); // → false
Довольно полезно, мне кажется. См. интерактивное демо, созданное Claude Code.
Предположим, что мы разрабатываем функцию поиска на странице, куда пользователи вводят свои поисковые запросы. И мы хотим реализовать это как поиск с помощью регулярных выражений (RegEx). Здесь есть определенная опасность, поскольку некоторые символы, которые может ввести пользователь, являются “специальными” символами в регулярных выражениях, например, символ $ соответствует последнему символу или типа того. Так что, если пользователь ищет $9, и мы просто пропускаем это через регулярное выражение, может возникнуть ошибка. Какие символы нужно “экранировать”, чтобы это исправить, зависит от конкретной реализации регулярного выражения.
Для решения этой проблемы у нас теперь есть RegExp.escape():
const query = userInput; // e.g. "$5.00 (off!)" // ❌ ДО — ломается при любом специальном символе const badRe = new RegExp(query, "g"); // ✅ ES2025 — один метод, проблема решена const goodRe = new RegExp(RegExp.escape(query), "g");
Также были внесены изменения в работу “флагов” внутри регулярных выражений. Очень распространенный флаг — это флаг “i”, означающий нечувствительность к регистру. Т.е., если регулярное выражение заканчивается на /i, все выражение нечувствительно к регистру. Но что, если мы хотим, чтобы нечувствительной к регистру была только определенная часть регулярного выражения? Теперь мы можем заключить такую часть в скобки и добавить флаг “i” в начало:
// Старый способ — нельзя смешивать разную чувствительность к регистру /[a-z]+@[A-Z]+/i // флаг 'i' применяется ко всему выражению // ES2025 — встроенные групповые модификаторы /(?i:[a-z.]+)@(?-i:[A-Z]+)\.(?i:com|org)/ //^^^^ нечувствительная к регистру часть // ^^^^^ чувствительная часть // ^^^ нечувствительная
Любимая многими асинхронная модель выполнения программ (промисы) получила небольшое обновление, которое упрощает обработку ошибок - Promise.try(). Раньше ошибки в функциях возникали синхронно или асинхронно, и их приходилось обрабатывать отдельно, но теперь это можно делать одновременно:
// Функция, которая может быть синхронной или асинхронной function loadUser(id) { if (!id) throw new Error("No ID"); // синхронный выброс исключения return fetch(`/api/users/${id}`); // асинхронная функция } // ❌ BEFORE — two separate error paths let p; try { p = loadUser(id); // ловим синхронный выброс здесь } catch (e) { handleError(e); } p?.catch(e => handleError(e)); // и асинхронное отклонение здесь // ✅ ES2025 — одна строка, один .catch() Promise.try(() => loadUser(id)) .then(user => render(user)) .catch(err => showError(err)); // ловим обе ошибки
Атрибуты импорта (import attributes) — мой личный фаворит. Серьезно! Во-первых, мне нравится идея просто импортировать JSON как JSON, вместо того чтобы получать его, анализировать и все такое:
import data from "./file.json" with { type: 'json' }
Часть после with называется “атрибутами импорта”, и у них есть еще несколько скрытых функций, о которых мы поговорим позже.
Импорт JSON кажется мне удобным и позволяет сэкономить пару строк кода, но, честно говоря, у него есть несколько существенных недостатков, на которые указывает Джейк Арчибальд в своей статье “Импорт и получение JSON”. Один из главных: неудачный импорт “приводит к краху всего графа модулей (module graph)”, что, мягко говоря, очень плохо. Вместо этого можно использовать динамический import() для перехвата ошибки:
try { const { default: data } = await import(url, { with: { type: 'json' }, }); } catch (error) { // Логика обработки ошибки }
Но это не так хорошо, как JSON, который мы получаем, просто используя fetch. Джейк завершает свою статью, отмечая, что импортированные данные “будут храниться в графе модулей на протяжении всей жизни страницы” и не будут подвергаться сборке мусора, как данные после выполнения fetch. В любом случае, будьте осторожны.
Однако с помощью атрибутов импорта можно импортировать не только JSON. Когда я говорю, что атрибуты импорта — моя личная любимая возможность, я в основном имею в виду, что мне очень нравится возможность импортировать CSS таким способом:
import componentStyles from "./component.css" with { type: "css" };
Я подробно рассматриваю это в статье “Хорошая архитектура чистого (vanilla) приложения с помощью веб-компонентов и CSS-модулей”. Мне очень нравится, что мы можем хранить CSS-файлы внутри других CSS-файлов, которые могут находиться в папке рядом с JavaScript-компонентом:
import sheet from './styles.css' with { type: 'css' }; class MyComponent extends HTMLElement { constructor() { super(); const shadowRoot = this.attachShadow({ mode: 'open' }); shadowRoot.adoptedStyleSheets = [sheet]; } // ...
Это далеко не все, что есть в ES2025, в сети есть много статей, посвященных этой теме. Мне очень помогла статья Мэтью Тайсона “ECMAScript 2025: лучшие новые возможности JavaScript” для InfoWorld. В ней, например, есть информация о Float16Array, что немного выходит за рамки моей компетенции, но касается обмена точности на использование памяти, когда это необходимо.
Пока еще начало 2026 года, но мы ожидаем ежегодный релиз ECMAScript в середине года, как и в предыдущие годы. Вот то, что уже находится на 4-й стадии и, вероятно, войдет в релиз.
Это, безусловно, самая захватывающая и полезная новинка в JavaScript за последнее время. Вкратце, ее можно описать так: “Теперь даты и время в JavaScript работают отлично, библиотеки не нужны”. Долгое время эту проблему решали такие большие, но качественные библиотеки, как Moment, заставляя разработчиков выбирать между производительностью и удобством использования 😬.
На момент написания этой статьи Safari — последний браузер без поддержки Temporal API, но работа над этим ведется, и сейчас он находится на стадии технической предварительной версии (TP), так что до его выхода осталось совсем чуть-чуть.
Сейчас очень легко узнать время в конкретном часовом поясе. Библиотеки не нужны.
const now = Temporal.Now.zonedDateTimeISO("America/New_York"); // Программная дата console.log(now.toString()); // Или более читаемый формат console.log(now.toLocaleString());
Меня очень позабавило, что во встречах TC39 есть небольшой фрагмент кода для запуска в консоли инструментов разработчика в браузере, который показывает, когда состоится предстоящая встреча в вашем текущем часовом поясе:
Temporal.ZonedDateTime.from('2026-03-10T10:00[America/New_York]') .withTimeZone(Temporal.Now.timeZoneId()) // ваша временная зона .toLocaleString();
Это просто здорово.
Temporal умеет миллион вещей, но вот еще парочка, которые раньше были ужасны.
Например, если мы “прибавим один месяц” к последнему дню января, то получим совершенно странный результат:
const date = new Date(2026, 0, 31); // Jan 31 date.setMonth(date.getMonth() + 1); // добавляем один месяц console.log(date.toDateString()); // Sun Mar 03 2026 ❌ 😬
Но с прекрасным Temporal API такой проблемы нет:
const jan31 = Temporal.PlainDate.from("2026-01-31"); const feb = jan31.add({ months: 1 }); console.log(feb.toString()); // 2026-02-28 ✅
Кроме того, теперь можно легко сравнивать даты и время:
const a = Temporal.Duration.from({ hours: 25 }); const b = Temporal.Duration.from({ days: 1 }); const cmp = Temporal.Duration.compare(a, b, { relativeTo: Temporal.Now.plainDateISO() }); console.log(cmp); // 1 (25 часов > 1 день) ✅
Появилось новое ключевое слово using для async/await, обеспечивающее очистку памяти. [Symbol.dispose]() (или [Symbol.asyncDispose]()) гарантированно вызывается во время выполнения, когда переменная выходит из области видимости:
class FileHandle { constructor(path) { this.path = path; console.log(`Opened ${path}`); } async write(data) { // Записываем данные } async [Symbol.asyncDispose]() { await someFlushOperation(); console.log(`Flushed and closed ${this.path}`); } } async function saveData() { await using file = new FileHandle("output.txt"); await file.write("hello world"); // Файл автоматически закрывается здесь, даже при ошибке }
Ключевое слово using хорошо подходит для одного ресурса, но также есть DisposableStack для нескольких ресурсов, которые необходимо обязательно очистить:
async function runJob() { await using stack = new AsyncDisposableStack(); const db = stack.use(await openDatabase()); const file = stack.use(new FileHandle("output.txt")); const tmpDir = stack.defer(async () => removeTempDir("/tmp/job")); // Выполняем работу await file.write(await db.query("SELECT * FROM jobs")); // Все три очищаются здесь, в обратном порядке, даже при ошибке }
Array.fromAsync впервые был выпущен в 2024 году, но, по всей видимости, возникли некоторые проблемы со спецификацией, поскольку он, судя по всему, войдет только в спецификацию ECMA2026. Он ждет каждое значение, возвращаемое yield, в процессе обхода асинхронного итератора, собирая результаты в обычный массив. Без этого нужно выполнять цикл и добавлять элементы вручную:
async function* fetchNumbers() { yield 1; await new Promise(r => setTimeout(r, 100)); // имитация асинхронной задержки yield 2; await new Promise(r => setTimeout(r, 100)); yield 3; } const numbers = await Array.fromAsync(fetchNumbers()); console.log(numbers); // [1, 2, 3]
Это, вероятно, наиболее полезно, когда ожидается вызов функции, которая перебирает асинхронные функции, каждая из которых возвращает (yield) свой результат, например, для пагинации или чего-то подобного. Вместо yield можно передать массив промисов, которые вернутся после разрешения (resolve).
И, говоря о пагинации, Iterator.concat - это новая функция, которая позволяет лениво оценивать каждый перебираемый элемент. Таким образом, вместо того, чтобы заранее помещать все элементы в массив для итерации, можно выполнять итерацию лениво - при прерывании итерации экономится память, которая бы использовалась для заполнения массива для итерации на раннем этапе:
const page1 = [{ id: 1 }, { id: 2 }][Symbol.iterator](); const page2 = [{ id: 3 }, { id: 4 }][Symbol.iterator](); const page3 = [{ id: 5 }, { id: 6 }][Symbol.iterator](); for (const item of Iterator.concat(page1, page2, page3)) { process(item); // обрабатываем страницы лениво }
Суть в том, что теперь можно с уверенностью определить, является ли значение подлинным объектом Error, а не просто объектом, внешне похожим на него. Это полезно в таких ситуациях, как централизованный сервис обработки ошибок, который потенциально получает ошибки из таких мест, как веб-воркеры (web workers) или iframe, которые представляют собой разные “области” (realms) и могут все испортить.
Наверняка вы это видели: console.log(0.1 + 0.2), и результат получается очень странным: 0.30000000000000004. Долгая история. Ну, просто попробуйте console.log(Math.sumPrecise([0.1, 0.2])) (пока это поддерживается только в Firefox), и вы увидите, что результат… такой же.
Но, судя по всему, для некоторых вещей это все равно полезно 🤷♀️
Здорово, что теперь есть простой и понятный метод для решения подобных задач:
const val = "Frontend Masters!"; const textEnc = new TextEncoder(); const bytes = textEnc.encode(val); console.log(bytes.toBase64()); // 'RnJvbnRlbmQgTWFzdGVycyE=' console.log(bytes.toHex()); // '46726f6e74656e64204d61737465727321'
React 19 вышел в декабре 2024 года, с тех пор прошло много времени. Сейчас у нас версия 19.2, и, насколько мне известно, общедоступной информации о том, что будет в React 20, не так уж много.
React 19 стал довольно масштабным релизом, включающим в себя так называемые “Серверные компоненты” (RSC), компилятор React и серверные операции. Вот их краткое описание:
RSC: если можно использовать Node-сервер, то, возможно, некоторые компоненты, которые обычно включаются в клиентскую часть JavaScript, можно перенести на сервер, передавая клиенту только необходимые данные
серверные операции: позволяют вызывать на клиенте функции, которые выполняются на Node-сервере. Обработка форм — классический пример
компилятор: традиционно некоторые оптимизации производительности оставлялись на усмотрение человека. Вы эксперт по использованию useMemo? Я тоже нет. Компилятор React выполняет эти оптимизации автоматически. Небольшое повышение производительности за счет небольшого усложнения сборки
Конечно, есть и множество других мелких деталей, но в общих чертах, это основные вещи, о существовании которых вам следует знать. React Native вышел в версии 0.83, о которой я, к сожалению, знаю очень мало, но мне кажется примечательным, что они (в некотором роде) “анонсировали” версию 1.0, что, должно быть, очень приятно всем, кто участвовал в разработке React Native в течение десяти лет.
Что насчет серверных технологий React, которые только что появились? Что ж, в прошлом году были обнаружены две очень серьезные уязвимости безопасности, что справедливо многих напугало.
Vue 3.5 сохраняет стабильность, а Vue 3.6 перешел в стадию альфа -тестирования с новой функцией, которую можно включить по желанию, — Vapor Mode, обеспечивающей значительное повышение производительности (“сопоставима с Solid и Svelte 5”).
В 2024 году у нас был хороший обзор всей экосистемы Vue. Но, будучи совершенно незнакомым с Vue человеком, мне немного сложно понять ситуацию в 2025/2026 годах. Очевидно, что Эван Ю — главный человек в этой сфере, но он руководит VoidZero (“компания по разработке инструментов JavaScript”), которая сейчас активно работает над Vite+ - целым рядом крупных проектов, таких как сам Vite, форматирование, линтинг, тестирование и т.д. Ничто из этого не является специфичным для Vue, и я представляю, как сложно сосредоточиться на самом Vue, когда имеется много других проектов 🤷♀️.
Пожалуй, основной метафреймворк Vue — это Nuxt, который вышел в версии 4.0. “Хранителями” Nuxt являются NuxtLabs, недавно приобретенные Vercel. Vercel не хочет “владеть” Nuxt… вроде. Часть меня радуется, что у Nuxt есть теоретически стабильное будущее, а другая часть меня удивляется, что VoidZero использует практически все свои инструменты JavaScript, кроме метафреймворка на своем родном языке. Pinia, похоже, является преобладающей библиотекой управления состоянием для Vue, вышел в версии 3 и отказался от поддержки Vue 2.
Svelte уверенно движется вперед с версией 5. Это было крупное обновление в мире Svelte, включающее так называемый “Rune API”, который полностью изменил принцип работы реактивности, сделав ее более “мелкозернистой”, как они говорят, а значит, более эффективной и быстрой. Честно говоря, я не так уж много знаю о Svelte или SvelteKit, за исключением того, что они тоже являются частью Vercel и пользуются большой популярностью у пользователей.
Самые большие среды выполнения JavaScript, безусловно, встроены в браузеры. Но что касается сред, которые можно выбирать и запускать самостоятельно для выполнения собственных задач, Node по-прежнему остается доминирующим игроком, хотя у него есть два интересных конкурента. Мы уже рассматривали случаи, когда Deno или Bun могут стать достойной альтернативой Node. В последнее время между ними наблюдается больше сближения, чем расхождения, они поддерживают TypeScript нативно, также растет поддержка канонического Node.
Пожалуй, самая важная последняя новость в Node.js — это возможность нативного запуска файлов TypeScript:
node my-script.ts
Это работает начиная с Node 22.18.0, флаг --experimental-strip-types больше не нужен. Обратите внимание, что типы по-прежнему удаляются, а это значит, что Node не будет предупреждать о проблемах в типизации.
Самые важные новости из мира Node.js, как правило, касаются простых, но важных базовых вещей, таких как улучшения безопасности, производительности и согласованности с веб-API браузеров.
Лично я очень доволен прогрессом Node.js. Я работал над проектами, перешедшими во встроенный в Node.js инструмент для запуска тестов, что приятно и позволяет уменьшить количество зависимостей. Я приветствую работу Node.js над моделью разрешений, благодаря которой он стал более удобен в ситуациях с ненадежным кодом.
Главным релизом Bun стал 1.3, в котором появилось множество функций, улучшающих опыт разработки. Очень удобно, что можно запустить полнофункциональный сервер разработки, просто указав путь к HTML-файлам:
bun './**/*.html'
Это также выполняет обработку и сборку файлов, что делает Bun альтернативой Vite в данном контексте.
Пожалуй, самая важная новость о Bun — это то, что Anthropic (владеющая, например, Claude) приобрела Bun в конце прошлого года. Думаю, общее мнение таково, что это хорошая новость для Bun, обеспечивающая ей стабильное и хорошо финансируемое будущее.
Как правило, люди выбирают Bun из-за скорости. Он устанавливается из npm очень быстро и в целом работает быстрее во всех отношениях. Ценой некоторого снижения стабильности.
Deno уже некоторое время находится в версии 2. Насколько мне известно, он обладает полной совместимостью с Node.js и является самым стабильным из трех. Благодаря спецификатору npm: в пакетах, он также теперь полностью совместим с npm.
Я думаю, что люди в основном выбирают Deno из-за стабильности и архитектуры, ориентированной на безопасность. Они сами это четко заявляют:
Deno по умолчанию безопасен. Если вы специально не включите эту функцию, программа, запущенная с использованием Deno, не будет иметь доступа к конфиденциальным API, таким как доступ к файловой системе, сетевому подключению или среде выполнения. Вам необходимо явно предоставить доступ к этим ресурсам с помощью флагов командной строки или запроса разрешений во время выполнения. Это существенное отличие от Node, где зависимостям автоматически предоставляется полный доступ ко всем системным операциям ввода-вывода, что потенциально может привести к скрытым уязвимостям в вашем проекте.
Это хороший дизайн.
Vite стал доминирующим инструментом сборки в экосистеме JavaScript. Видимо, он оказался в нужном месте в нужное время! Хотя его создали те же разработчики, что и Vue, Vite — это инструмент сборки, который подходит практически для любого фронтенд-проекта. Мне очень нравится их подход, при котором локальная разработка работает за счет обновления только небольших частей кода, изменяющихся в процессе работы, без необходимости полной сборки, но при этом обеспечивает сборку, пригодную для продакшена, по запросу.
Vite недавно обновился до версии 8. Это стало значительным релизом, поскольку вместо использования стороннего инструмента для сборки пакетов Rollup, теперь используется Rolldown — собственный инструмент сборки. Это соответствует стремлению Vite к созданию более “унифицированной цепочки инструментов”, как они сами это называют. Они могут использовать общие инструменты во всех своих продуктах (например, парсер), что делает весь процесс более предсказуемым. Они называют всю эту цепочку инструментов Vite+, которая включает в себя продвинутый сервер разработки от Vite, форматирование, линтинг, проверку типов, тестирование, запуск задач, поддержку монорепозиториев и сборку. Это очень много!
Они работают над дальнейшим развитием проекта, создав “платформу развертывания” под названием Void, которая использует сервисы Cloudflare для хостинга, хранения данных, облачных функций и всего остального.
“База данных, хранилище ключ-значение, объектное хранилище, ИИ, аутентификация, очереди и задачи cron. Все встроено. Импортируйте то, что вам нужно, пропускайте то, что не нужно”.
В настоящее время почти все фреймворки используют Vite: Astro, SolidStart, SvelteKit, Nuxt и т.д. Заметным исключением является Next.js, который использует webpack и переходит на Turbopack (см. следующий раздел). Но недавно Cloudflare перенесла Next.js AI на Vite, что является довольно спорным шагом.
Turbopack — это сборщик от Vercel, который стал сборщиком по умолчанию для Next.js, начиная с версии 16. Turbopack — это проект на Rust, который, как предполагается, работает в 5-10 раз быстрее, чем Webpack в предыдущих версиях Next.js. Насколько я понимаю, на данный момент Turbopack предназначен специально для Next.js.
Webpack по-прежнему широко используется, и у него есть план развития на 2026 год, который включает в себя множество идей по уменьшению необходимости в различных загрузчиках и другим упрощениям. Это долгожданное обновление, поскольку общее мнение о Webpack заключается в том, что он слишком сложен.
TypeScript только что вышел в версии 6. Они говорят, что версия 7 выйдет в середине 2026 года, и это будет масштабный релиз, включающий переход на новый компилятор на основе Go. Главная цель версии 6 — подготовка к этим изменениям. Думаю, в рассылке Bytes было хорошее краткое резюме:
Строгий режим теперь включен по умолчанию, значением
moduleпо умолчанию являетсяesnext, значениеtargetсоответствует спецификации текущего года (в настоящее времяes2025), и вtypesтеперь по умолчанию используется пустой массив вместо того, чтобы аккумулировать все данные изnode_modules/@types. Одно только последнее изменение сломает многие проекты, но также должно ускорить их работу на 20–50%.
Вероятно, вам стоит подготовиться к переходу на версию 7, если вы заинтересованы в улучшении производительности примерно на 10% (прим. пер.: команда TypeScript заявляет о десятикратном ускорении) в таких приложениях, как VS Code и Playwright.
Примечательно, что TypeScript стал языком программирования номер один на GitHub, показав рост на 66% по сравнению с прошлым годом.
Несколько лет назад обсуждалась возможность добавления типов непосредственно в JavaScript. Возможно, это позволило бы получить некоторые преимущества TypeScript без необходимости использования компилятора. Однако, похоже, эта идея не получила широкого распространения и вряд ли в обозримом будущем мы сможем обойтись без TypeScript.
Пожалуй, это самое подходящее место, чтобы упомянуть об этом, учитывая огромный объем открытого исходного кода TypeScript, доступного для обучения моделей. ИИ сегодня очень хорошо справляется с написанием кода, особенно на TypeScript. Говорят, что 92% разработчиков в той или иной степени используют ИИ для написания кода, что является поразительным ростом и, безусловно, самой важной темой в разработке на данный момент.
Основные фреймворки для тестирования JavaScript остаются прежними: Jest, Jasmine, Mocha и т.д. Но произошли некоторые изменения, особенно с ростом популярности Vite - их фреймворк для тестирования Vitest стал очень востребованным. Он совместим с Jest, поэтому перенос тестов на него, как правило, довольно прост, и он намного быстрее (и, на мой взгляд, выглядит лучше). Vitest также имеет “режим браузера”, т.е. он может запускать тесты в реальном браузере, что очень важно для тестирования компонентов. Playwright, похоже, тоже переживает бум популярности, используется в основном для сквозного тестирования, и, кажется, стал популярнее, чем Puppeter или Cypress (по крайней мере, по моим ощущениям).
Next.js вышел в версии 16, это первый релиз с Turbopack в качестве сборщика по умолчанию. Лично мне нравится такой прогресс, но я отключил его в своих проектах, так как миграция оказалась сложной. Однако заметный шаг вперед — это улучшения в логировании и обработке ошибок. Эта версия Next.js по умолчанию использует компилятор и серверные компоненты React, что теоретически обеспечивает повышение производительности в целом, но результаты кажутся более сложными и неоднозначными (см. здесь, здесь, здесь и здесь).
Если в вашем приложении Next.js активно используется ИИ, обратите внимание, что Vercel теперь предоставляет сервер MCP. При его подключении ИИ будет работать на вашем сайте намного умнее.
Next.js 16 также работает на React 19, что означает поддержку компонента <ViewTransition>, о котором мы говорили здесь.
Несколько лет назад Remix был куплен компанией Shopify. Затем вышла версия 2, после чего было объявлено, что вместо Remix v3 будет просто React Router v7. Сейчас Remix v3 все еще планируется и находится в активной разработке. Главное, что React больше не будет его частью:
Мы создаем собственную компонентную модель, которая по ощущениям ближе к веб-технологиям, чем все, что мы видели раньше.
Возможно, вся эта неразбериха с Remix пошла на пользу вселенной TanStack, которая представляет собой набор инструментов, включая довольно популярный маршрутизатор. И, как и Remix до него, этот маршрутизатор также превратился в полноценный фреймворк.
У нас много материалов от Адама Ракиса, посвященных знакомству с миром TanStack.
Astro успешно развивается уже много лет и не собирается сбавлять обороты. В этом году ее приобрела Cloudflare, что в целом кажется хорошим знаком, поскольку даже на основе качественных фронтенд-фреймворков, как известно, сложно построить сильную бизнес-модель, и решение, похоже, заключается в партнерстве с серьезным хостинг-провайдером. Его уже используют для создания необычного клона WordPress.
Если вы хотите создать сайт, который по умолчанию является статическим, но при этом использует современную компонентную архитектуру на основе JavaScript-фреймворков и позволяет легко реализовать более динамическое поведение, то Astro — это золотой стандарт и, на мой взгляд, отличный выбор.
Последняя версия Astro — 6.0, в которой появились расширенные функции, такие как настройка используемой среды выполнения в процессе разработки, политика безопасности контента и экспериментальный более быстрый компилятор. Вскоре за ней последовал релиз 6.1 с множеством небольших приятных улучшений конфигурации и т.п., что доказывает стремление команды создать хороший фреймворк.
В мире npm, похоже, ничего особенного не происходит. Прошло 6 лет с тех пор, как Microsoft/GitHub приобрели его, и, кажется, он работает нормально. Сам GitHub испытывает трудности с бесперебойной работой.
Очень неприятными для npm были инциденты в цепочке поставок, такие как s1ngularity, который украл учетные данные/токены/файлы конфигурации пользователей и опубликовал их на GitHub 😳. Затем был debug/chalk с вредоносными обновлениями пакетов, которые могли перенаправлять криптовалютные транзакции на кошелек злоумышленников. А еще был червь Шай-Хулуд (извините, черви, во множественном числе), представлявший собой самовоспроизводящуюся вредоносную программу для кражи учетных данных, версия 2.0 которой перезаписывала/удаляла каждый файл в домашнем каталоге пользователя. Эта атака затронула 796 пакетов npm с более чем 20 миллионами загрузок в неделю… ничего себе! Не самый лучший последний год для npm с точки зрения безопасности.
Если у вас есть серьезные приложения, использующие npm в производственной среде, возможно, стоит обратить внимание на такой инструмент, как Socket, для обеспечения качественной защиты.
Освоение фундаментальных навыков работы со всеми упомянутыми в статье вещами пригодится вам независимо от изменений в инструментах, фреймворках и т.д. И, осмелюсь сказать, чем больше ИИ помогает нам с кодом, тем больше нам нужны такие люди, которые действительно знают, что делают, и могут помочь планировать, направлять, формировать, тестировать, проектировать и применять “хороший вкус” к коду, независимо от способа его создания.