javascript

10 веб-API, заменяющих многие библиотеки JavaScript

  • пятница, 27 марта 2026 г. в 00:00:07
https://habr.com/ru/articles/1015134/

Современные браузеры тихо съедают экосистему JavaScript живьем.

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

Такой подход обходится дороже, чем кажется. Каждый дополнительный пакет увеличивает вес сборки, затраты на обслуживание, частоту обновления версий и риск отказа от проекта в долгосрочной перспективе. Нативные API-интерфейсы стоят пользователям 0 КБ данных, работают глубоко в движке (часто вне основного потока (main thread)) и используют оптимизации, недоступные библиотекам.

Если вы пытаетесь уменьшить размер сборки, улучшить производительность во время выполнения или избавиться от лишних зависимостей, стоит знать, какие “дефолтные пакеты” стали необязательными.

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

1. Fetch API

Заменяет: Axios, jQuery.ajax, request и др.

В какой-то момент Axios стал дефолтным клиентом HTTP для каждого JS-проекта. В 2016 г. это объяснялось просто: XMLHttpRequest был неудобным, а axios предоставлял чистый основанный на промисах API с перехватчиками (interceptors), автоматическим разбором (parsing) JSON и возможностью отмены запросов.

Штука в том, что fetch доступен во всех основных браузерах с 2017 г. Он основан на промисах, поддерживает потоковую передачу данных (streaming), обрабатывает каждый метод HTTP и работает как в браузере, так и в Node.js. Однако многие продолжают по привычке устанавливать axios.

Типичная настройка axios:

import axios from 'axios';

const api = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000,
  headers: { 'Content-Type': 'application/json' }
});

// GET
const { data: users } = await api.get('/users');

// POST
const { data: newUser } = await api.post('/users', {
  name: 'Marvel',
  role: 'developer'
});

Нативный эквивалент:

const BASE_URL = 'https://api.example.com';

// GET
const res = await fetch(`${BASE_URL}/users`);
const users = await res.json();

// POST
const res2 = await fetch(`${BASE_URL}/users`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Marvel', role: 'developer' })
});
const newUser = await res2.json();

Единственное важное отличие состоит в том, что fetch не выбрасывает статус-код ошибки HTTP. Ответ 404 успешно разрешает промис, поэтому необходимо явно проверять res.ok. axios в этом случае выбрасывает исключение.

Однако эта задача решается всего одной строкой кода:

if (!res.ok) throw new Error(`HTTP ${res.status}`);

Для отмены запроса fetch используется AbortController:

const controller = new AbortController();

fetch('/api/data', { signal: controller.signal });

// Отмена запроса
controller.abort();

Поддержка браузеров: все современные браузеры.

axios по-прежнему выигрывает в следующих случаях:

  • при разработке простого приложения с несколькими вызовами API fetch может быть излишне многословным

  • при разработке большого приложения с централизованной обработкой аутентификации, стратегиями повторных запросов и преобразованиями запроса/ответа между несколькими конечными точками паттерн перехватчиков axios оправдывает свой вес в сборке приложения

2. FormData API

Заменяет: библиотеки сериализации (serialization) форм, утилиты для загрузки файлов и др.

До FormData сбор значений полей формы для отправки означал либо использование автоматической сериализации Axios, либо написание собственной логики, либо, чаще всего, использование React Hook Form или Formik для обработки всего процесса от валидации до отправки, чтобы вообще не приходилось думать о сериализации.

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

Ручной подход выглядел примерно так:

function getFormValues(form) {
  const data = {};
  const inputs = form.querySelectorAll('input, select, textarea');

  inputs.forEach(input => {
    if (input.type === 'checkbox') {
      data[input.name] = input.checked;
    } else if (input.type === 'file') {
      data[input.name] = input.files[0];
    } else {
      data[input.name] = input.value;
    }
  });

  return data;
}

А что насчет полей с множественным выбором (multiselect), радиокнопок или отключенных (disabled) полей? А что насчет загрузки файлов? Нужно прочитать файл, закодировать его, установить правильные заголовки типа содержимого и все такое (что довольно сложно).

FormData позволяет делать так:

const form = document.querySelector('#myForm');
const formData = new FormData(form);

Вот и все. FormData извлекает каждое поле, обрабатывает файлы автоматически, и когда мы передаем данные в fetch, браузер сам устанавливает тип содержимого multipart/form-data с правильной границей (boundary):

const form = document.querySelector('#upload-form');

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const formData = new FormData(form);

  // Файлы, текстовые поля, чекбоксы - все обрабатывается автоматически
  await fetch('/api/upload', {
    method: 'POST',
    body: formData  // заголовок Content-Type устанавливается автоматически
  });
});

FormData может формироваться программно:

const formData = new FormData();
formData.append('name', 'Marvel');
formData.append('avatar', fileInput.files[0]);
formData.append('tags', JSON.stringify(['react', 'typescript']));

Поддержка браузеров: все основные браузеры с 2015 г. Этот API старше большинства пакетов npm, предназначенных для его замены.

Случаи использования библиотек: FormData просто извлекает значения и отправляет их. Вот и все. Он не валидирует входные данные, не управляет состоянием между шагами и не отображает поля условно в зависимости от того, что пользователь выбрал три шага назад. Если вы создаете многоэтапную форму оформления заказа или регистрации, где поле “A” определяет, что отобразится на этапе “Б”, React Hook Form и Formik по-прежнему оправдывают свой вес в сборке.

3. URL API

Заменяет: URL.js и др.

Разбор URL в JavaScript раньше означал одно из двух: регулярное выражение или npm install. Пакет URL.js все еще скачивается 1,4 млн раз в неделю, что, в конечном счете, является результатом мышечной памяти.

Вот как разработчики писали код раньше (и некоторые все еще пишут):

// Регулярное выражение
function getQueryParam(url, param) {
  const regex = new RegExp('[?&]' + param + '=([^&#]*)');
  const match = url.match(regex);
  return match ? decodeURIComponent(match[1]) : null;
}

// Разделение строки
const query = window.location.search.substring(1);
const pairs = query.split('&');
const params = {};
pairs.forEach(pair => {
  const [key, value] = pair.split('=');
  params[key] = decodeURIComponent(value);
});

API URL и URLSearchParams обрабатывают все это нативно:

const url = new URL('https://example.com/search?q=web+apis&page=2&sort=recent#results');

url.hostname;                   // 'example.com'
url.pathname;                   // '/search'
url.hash;                       // '#results'

url.searchParams.get('q');      // 'web apis' (декодируется автоматически)
url.searchParams.has('sort');   // true

// Модификация параметров
url.searchParams.set('page', '3');
url.searchParams.append('filter', 'new');
url.searchParams.delete('sort');

url.toString();
// 'https://example.com/search?q=web+apis&page=3&filter=new#results'

Разбор, кодирование, декодирование и модификация - все обрабатывается автоматически. URLSearchParams также позволяет формировать строки запроса (query string) с нуля:

const params = new URLSearchParams({ q: 'hello world', page: '1' });
params.toString(); // 'q=hello+world&page=1'

Поддержка браузеров: все основные браузеры по состоянию на 2026 г. URLSearchParams - с апреля 2018 г. Также работает в Node.js 10+.

Случаи использования библиотек: библиотеки вроде URL.js больше почти не нужны. Нативные API охватывают 95% нужд разработчиков.

4. Popover API

Заменяет: Tippy.js, Floating UI и др.

Всплывающие подсказки (tooltips), выпадающие меню (dropdowns) и поповеры (popovers) были проблемой в JS на протяжении долгого времени. Нам нужен элемент, располагающийся поверх всех других элементов, позиционируемый относительно другого элемента, скрывающийся при клике пользователя за его пределами и доступный с помощью клавиатуры. Это сложно реализовать самостоятельно, поэтому разработчики прибегали к помощи таких библиотек, как Tippy.js или Floating UI (@floating-ui/react скачивается 10 млн раз в неделю).

Пример базового поповера, реализованного с помощью Floating UI в React:

import { useFloating, offset, flip, shift, autoUpdate } from '@floating-ui/react';
import { useState } from 'react';

function Popover() {
  const [isOpen, setIsOpen] = useState(false);

  const { refs, floatingStyles } = useFloating({
    open: isOpen,
    placement: 'bottom',
    middleware: [offset(8), flip(), shift()],
    whileElementsMounted: autoUpdate,
  });

  return (
    <>
      <button
        ref={refs.setReference}
        onClick={() => setIsOpen(!isOpen)}
      >
        Toggle popover
      </button>
      {isOpen && (
        <div ref={refs.setFloating} style={floatingStyles}>
          Popover content
        </div>
      )}
    </>
  );
}

Это минимальная настройка, которая не включает “легкую” отмену (light dismiss), захват фокуса и обработку клавиатуры.

Popover API позволяет реализовать все это с помощью атрибутов HTML:

<button popovertarget="my-popover">Toggle popover</button>
<div id="my-popover" popover>Popover content</div>

Две строки, и браузер обрабатывает рендеринг на верхнем уровне (top layer) (нет проблемы правильного z-index), легкую отмену (клик за пределами или нажатие Esc для закрытия), управление фокусом (переключение между элементами внутри поповера с помощью Tab) и полная доступность с помощью клавиатуры.

Видимостью поповера можно управлять программно:

const popover = document.querySelector('#my-popover');

popover.showPopover();   // отображение
popover.hidePopover();   // скрытие
popover.togglePopover(); // переключение видимости

Для стилизации видимого поповера можно использовать псевдокласс :popover-open, а ::backdrop позволяет стилизовать подложку (overlay):

#my-popover {
  padding: 1rem;
  border: 1px solid #ddd;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

#my-popover::backdrop {
  background: rgba(0, 0, 0, 0.15);
}

#my-popover:popover-open {
  animation: fadeIn 0.2s ease-out;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}

Атрибут popover принимает два значения: auto (по умолчанию) - легкая отмена и автоматическое закрытие других поповеров. manual - поповер закрывается явно, что может быть полезным для важных уведомлений или многоэтапных форм.

Поддержка браузеров: все основные браузеры с января 2025 г. (Chrome 114+, Firefox 125+, Safari 17+).

Случаи использования библиотек: Popover API не обрабатывает позиционирование. Floating UI по-прежнему выигрывает в привязке поповера к определенной стороне триггера (trigger) с обнаружением пересечений с областью просмотра.

Anchor Positioning API скоро решит эту проблему, но его поддержка браузерами пока является неполной. Поэтому, если вам нужна всплывающая подсказка, которая автоматически перемещается снизу вверх при достижении края области просмотра, Floating UI по-прежнему остается актуальным. Но для меню действий, уведомлений, обучающих интерфейсов и базовых шаблонов раскрытия информации вполне достаточно встроенных возможностей браузера.

5. Clipboard API

Заменяет: clipboard.js, copy-to-clipboard и др.

Каждая кнопка “Копировать в буфер обмена”, которую вы когда-либо нажимали, вероятно, реализована с помощью пакетов clipboard.js или copy-to-clipboard. А до них использовался ужасный хак: создание скрытой <textarea>, вставка в нее текста, его программное выделение, выполнение document.execCommand('copy') и удаление <textarea> из DOM.

Таким был стандартный подход на протяжении многих лет, и он был очень хрупким:

function copyToClipboard(text) {
  const textarea = document.createElement('textarea');
  textarea.value = text;
  textarea.style.position = 'fixed';
  textarea.style.opacity = '0';
  document.body.appendChild(textarea);
  textarea.select();
  document.execCommand('copy');
  document.body.removeChild(textarea);
}

9 строк кода по манипуляции DOM для копирования строки. Причем, document.execCommand('copy) уже тогда давно был признан устаревшим.

Clipboard API заменяет все это одной строкой:

await navigator.clipboard.writeText('Hello, clipboard!');

Вот и все. Основанный на промисах, асинхронный, чистый. Чтение из буфера обмена не менее простое:

const text = await navigator.clipboard.readText();

Реализация кнопки копирования:

const copyBtn = document.querySelector('#copy-btn');

copyBtn.addEventListener('click', async () => {
  try {
    await navigator.clipboard.writeText(codeSnippet.textContent);
    copyBtn.textContent = 'Copied!';
  } catch (err) {
    copyBtn.textContent = 'Failed to copy';
  }
});

Важный момент: Clipboard API работает только в безопасном контексте (HTTPS) и требует, чтобы страница находилась в фокусе. Браузер обрабатывает разрешения автоматически. Это выигрыш в безопасности, а не ограничение.

Поддержка браузеров: writeText() и readText() поддерживаются всеми основными браузерами. Более продвинутые методы, такие как write() (для копирования изображений и насыщенного (rich) контента), имеют частичную поддержку.

Случаи использования библиотек: копирование сложных форматов, таких как HTML или изображения, с поддержкой всех браузеров. Для текста в 95% случаев достаточно нативного API.

6. ResizeObserver API

Заменяет: обработчики события изменения размеров окна, element-resize-detector, react-resize-detector и др.

Отслеживание размеров означает обработку события window.resize и надежды на лучшее. Проблема в том, что window.resize возникает при изменении размеров области просмотра, а не определенного элемента. Боковое меню может скрываться, а контейнер - перерисовываться.

Поэтому разработчики ищут обходные пути: периодически вызывают getBoundingClientRect(), откладывают (throttle) вызов обработчиков и повторно вычисляют (recalculate) макет (layout) вручную или используют пакеты, вроде element-resize-detector или react-resize-detector для того, что браузер должен делать нативно.

let lastWidth = 0;

window.addEventListener('resize', throttle(() => {
  const el = document.querySelector('.card');
  const width = el.getBoundingClientRect().width;

  if (width !== lastWidth) {
    lastWidth = width;
    updateLayout(width);
  }
}, 200));

Это дорого с точки зрения вычислений. Каждый вызов getBoundingClientRect() заставляет браузер повторно вычислять макет. Если это происходит в цикле или очень часто, начинаются проблемы с производительностью.

ResizeObserver наблюдает за отдельными элементами и вызывает коллбэк при изменении их размеров, независимо от причины:

const observer = new ResizeObserver((entries) => {
  for (const entry of entries) {
    const width = entry.contentBoxSize[0].inlineSize;

    if (width < 400) {
      entry.target.classList.add('compact');
    } else {
      entry.target.classList.remove('compact');
    }
  }
});

observer.observe(document.querySelector('.card'));

Браузер автоматически обрабатывает группировку (batching) и задержку (debouncing).

Реальные примеры использования - компоненты, учитывающие размер контейнера:

  • карточка, которая переключается с горизонтального на вертикальное отображение в зависимости от собственной ширины (а не от ширины области просмотра)

  • диаграмма, которая перерисовывается при уменьшении размера контейнера

  • панель мониторинга, которая адаптирует плотность контента в зависимости от имеющегося у нее пространства

Поддержка браузеров: все основные браузеры с июля 2020 г.

Случаи использования библиотек: запросы к контейнеру (container queries) (@container) в CSS сейчас обрабатывают большое количество случаев использования адаптируемых компонентов, которые раньше решал ResizeObserver. Если логика макетирования чисто визуальная (переключение стилей на определенных контрольных точках (breakpoints)), то лучшим выбором являются запросы к контейнеру. ResizeObserver незаменим, когда требуется выполнение определенной логики на JS в ответ на изменение размеров.

7. View Transitions API

Заменяет: Motion (Framer Motion), GSAP (для переключения страниц) и др.

Переключение страниц всегда было проблемой в JS. Но прежде чем двигаться дальше, необходимо отметить, что View Transitions API не заменяет Motion, он заменяет только его часть.

Предположим, нам нужна плавная (smooth) анимация перехода между страницами или скользящая (slide) анимация при переходе между представлениями (views). Скорее всего, мы воспользуемся одной из указанных выше библиотек. Это неплохой выбор, поскольку это наша основная библиотека для анимации, но взгляните на примерную реализацию требуемого функционала:

import { AnimatePresence, motion } from "framer-motion";
import { useLocation } from "react-router-dom";

function App() {
  const location = useLocation();

  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={location.pathname}
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        exit={{ opacity: 0, y: -20 }}
        transition={{ duration: 0.3 }}
      >
        <Routes location={location}>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </motion.div>
    </AnimatePresence>
  );
}

Эта обертка Motion используется для каждого перехода между страницами. Это работает и выглядит неплохо. Но мы используем около 30 КБ только для реализации плавного переключения между двумя страницами.

View Transitions API позволяет делать это нативно и бесплатно. Браузер делает снимок текущего DOM, мы обновляем его по своему усмотрению, и происходит анимация между старым и новым состояниями. Вот как это выглядит:

function navigateTo(url) {
  if (!document.startViewTransition) {
    updateContent(url);
    return;
  }

  document.startViewTransition(() => updateContent(url));
}

Браузер захватывает старое состояние, вызывает коллбэк, захватывает новое состояние и запускает плавный переход с помощью анимации CSS в композитном потоке (compositor thread). Стили:

::view-transition-old(root) {
  animation: fade-out 0.3s ease-in;
}

::view-transition-new(root) {
  animation: fade-in 0.3s ease-out;
}

Для отдельных элементов используется свойство view-transition-name:

.product-thumbnail { view-transition-name: hero-image; }
.product-hero { view-transition-name: hero-image; }

Браузер плавно трансформирует миниатюру в полный экран. Тот же эффект, который раньше требовал более 50 строк конфигурации Motion, теперь достигается всего тремя строками CSS.

Поддержка браузеров: Chrome 111+, Safari 18+, Firefox 144+ (октябрь 2025 г.). Переходы в пределах одного документа (SPA) поддерживаются всеми основными браузерами. Переходы между документами (MPA) работают в Chrome 126+ и Safari 18.2+.

Случаи использования библиотек: физика пружин, сложная хореография временной шкалы с чередующимися дочерними элементами, прерываемые анимации, анимации, управляемые жестами, точное управление воспроизведением (пауза, перемотка). View Transitions API заменяет 80% случаев: переходы на уровне страницы и трансформация элементов. Оставшиеся 20% - это то, где Framer Motion и GSAP оправдывают свой вес в сборке.

8. Dialog

Заменяет: react-modal, @headlessui/react dialogs, кастомные реализации модального окна.

Модальные окна - одна из тех вещей, которые кажутся простыми, пока не попробуешь сделать их правильно. При разработке модалки, среди прочего, необходимо учитывать следующее: захват и удержание фокуса внутри диалогового окна, предотвращение прокрутки тела окна, обработка нажатие клавиши Esc для закрытия окна, возврат фокуса к триггеру при закрытии окна и его отображение поверх всего остального с помощью z-index без конфликтов.

Именно поэтому существуют такие библиотеки, как react-modal и Headless UI. Они берут на себя логику доступности и многоуровневого интерфейса.

Типичная реализация модалки с помощью react-modal:

import Modal from 'react-modal';

Modal.setAppElement('#root');

function App() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Open modal</button>
      <Modal
        isOpen={isOpen}
        onRequestClose={() => setIsOpen(false)}
        contentLabel="Example Modal"
        style={{
          overlay: { backgroundColor: 'rgba(0, 0, 0, 0.5)' },
          content: { maxWidth: '500px', margin: 'auto' }
        }}
      >
        <h2>Modal Title</h2>
        <p>Some content here</p>
        <button onClick={() => setIsOpen(false)}>Close</button>
      </Modal>
    </>
  );
}

А вот реализация модалки с помощью нативного <dialog>:

<button onclick="document.getElementById('my-dialog').showModal()">
  Open modal
</button>

<dialog id="my-dialog">
  <h2>Modal Title</h2>
  <p>Some content here</p>
  <button onclick="this.closest('dialog').close()">Close</button>
</dialog>

Прим. пер.: есть более современный способ реализации модалки.

При вызове .showModal() браузер предоставляет хорошую модалку, псевдоэлемент ::backdrop для оверлея, захват фокуса (переключение между элементами с помощью Tab остается внутри dialog), обработка нажатия Esc для закрытия окна и восстановление фокуса. Все поведение, связанностью с доступностью, реализуемое react-modal с помощью JS, браузер предоставляет бесплатно.

Стили:

dialog {
  max-width: 500px;
  border: none;
  border-radius: 12px;
  padding: 2rem;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
}

dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(4px);
}

Для немодальных dialog (не блокируют страницу) используйте .show() вместо .showModal(). Прим. пер.: с появление Popover API не очень понятно, зачем нужны такие dialog. Возвращаемое из dialog значение можно получить через свойство returnValue:

dialog.addEventListener('close', () => {
  console.log(dialog.returnValue); // любое значение, переданное в dialog.close()
});

Поддержка браузеров: все основные браузеры (Chrome 37+, Firefox 98+, Safari 15.4+) с марта 2022 г.

Случаи использования библиотек: разработка дизайн-системы, требующей анимации открытия/закрытия dialog (dialog по умолчанию не анимируется, хотя это можно реализовать с помощью CSS-переходов; прим. пер.: скоро видимость dialog можно будет анимировать нативно), если требуется, чтобы модалка находилась вне дерева компонентов React по причинам управления состоянием (порталы). Для всего остального dialog делает больше, чем библиотеки, без каких-либо зависимостей.

9. Temporal

Заменяет: Moment.js, date-fns, Day.js, Luxon и др.

Обработка дат в JS сломана с самого начала. Объект Date мутирует при каждом взаимодействии с ним, индексация месяцев начинается с нуля (январь - 0, декабрь - 11), поддержка временных зон (timezone) отсутствует, а разбор одной и той же строковой даты в разных браузерах дает разные результаты. Честно говоря, я всегда устанавливал dayjs и притворялся, что Date не существует.

Temporal API решает все эти проблемы на уровне языка. Это не браузерный API, это возможность JS (TC39, стадия 3), которая предоставляет иммутабельные объекты даты/времени, первоклассную поддержку временных зон, наносекундную точность, отдельные типы для разных вещей, которые может означать “дата”.

Проблемы Date:

// Индексация месяцев начинается с 0. Это февраль, а не январь
const date = new Date(2025, 1, 14);

// Мутация. Это модифицирует исходный объект
date.setMonth(11);
// date теперь December 14, 2025. Оригинал потерян

// Хаос временных зон
new Date('2025-02-14'); // разные результаты в разных браузерах

С Temporal:

// Чисто, читаемо, иммутабельно
const date = Temporal.PlainDate.from('2025-02-14');

// Возвращается новый объект. Оригинал сохраняется
const later = date.add({ months: 3 }); // 2025-05-14
console.log(date.toString()); // все еще 2025-02-14

// Учет временной зоны
const meeting = Temporal.ZonedDateTime.from('2025-02-14T10:00[America/New_York]');
const inLagos = meeting.withTimeZone('Africa/Lagos');
console.log(inLagos.toString()); // 2025-02-14T16:00:00+01:00[Africa/Lagos]

// Разумная математика продолжительности
const start = Temporal.PlainDate.from('2025-01-01');
const end = Temporal.PlainDate.from('2025-03-15');
const diff = start.until(end);
console.log(diff.toString()); // P2M14D (2 месяца, 14 дней)

Temporal предоставляет разные типы для разных вещей: PlainDate для дат без времени, PlainTime для времени без дат, PlainDateTime для обоих, ZonedDateTime для учета временной зоны и Instant для точной отметки времени. Больше не надо гадать, что на самом деле представляет собой “дата” или объект Date.

Поддержка браузеров: в настоящее время поддержка является неполной, к сожалению. Firefox 139+ добавил поддержку в мае 2025 г., а Chrome 144 - в январе 2026 г. Но Safari и Edge пока не поддерживают эту фичу, следовательно, ее нельзя использовать в продакшне без полифилла (@js-temporal/polyfill или temporal-polyfill).

Случаи использования библиотек: на данный момент, если вам нужна кроссбраузерная поддержка, date-fns и dayjs по-прежнему являются лучшим выбором. Они легковесны и работают везде. Но не забывайте о Temporal. Как только Safari добавит его поддержку, Moment.js, Luxon и другие библиотеки для работы с датами станут кандидатами на удаление. Это единственный API в нашем списке, переход на использование которого пока не рекомендуется.

10. Geolocation API

Заменяет: IP-сервисы определения местоположения, geoip-lite, сторонние API геолокации и др.

Когда разработчикам нужно определить местоположение пользователя, первое, что приходит в голову, — обратиться к стороннему сервису. Они вызывают API геолокации по IP-адресу, такие как ipapi.co или ip-api.com, анализируют ответ и получают приблизительное местоположение на уровне города. Некоторые приложения устанавливают geoip-lite на сервер. Другие платят за такие сервисы, как MaxMind или IPinfo. И все это ради координат, которые могут отличаться на 50 километров, потому что определение местоположения по IP-адресу — это всего лишь приблизительная оценка.

Поиск по IP-адресу:

// Обращение к стороннему сервису
const res = await fetch('https://ipapi.co/json/');
const data = await res.json();

console.log(data.city);      // "Lagos" (возможно)
console.log(data.latitude);  // 6.4541 (примерно)
console.log(data.longitude); // 3.3947 (примерно)

Это внешний HTTP-запрос, зависимость от стороннего сервиса (с тарифными планами) и точность, в лучшем случае, на уровне города. Если пользователь подключен к VPN, мы получаем местоположение VPN-сервера. Если интернет-провайдер направляет трафик через другой регион, координаты будут неверными.

Geolocation API обеспечивает точность на уровне GPS непосредственно с устройства пользователя:

navigator.geolocation.getCurrentPosition(
  (position) => {
    console.log(position.coords.latitude);   // 6.5244 (точно)
    console.log(position.coords.longitude);  // 3.3792 (точно)
    console.log(position.coords.accuracy);   // точность в метрах
  },
  (error) => {
    console.error('Location access denied:', error.message);
  },
  { enableHighAccuracy: true }
);

Браузер запрашивает у пользователя разрешение, пользователь принимает решение, и устройство возвращает координаты, используя GPS, Wi-Fi или триангуляцию по вышкам сотовой связи — в зависимости от того, какой способ доступен. На мобильных устройствах с GPS точность составляет несколько метров.

Для приложений, требующих непрерывного отслеживания (навигатор, доставка, фитнес), watchPosition() запускает коллбэк при каждом перемещении устройства:

const watchId = navigator.geolocation.watchPosition(
  (position) => {
    updateMap(position.coords.latitude, position.coords.longitude);
  },
  (error) => console.error(error),
  { enableHighAccuracy: true }
);

// Остановка слежения
navigator.geolocation.clearWatch(watchId);

Модель обеспечения конфиденциальности также более удобна. Пользователь предоставляет или отклоняет разрешение через встроенный пользовательский интерфейс браузера, отсутствуют баннеры с запросом согласия на использование файлов cookie для отслеживания местоположения, а также нет скрытых запросов IP-адресов. Пользователь контролирует ситуацию.

Поддержка браузеров: все основные браузеры поддерживают эту технологию уже много лет. Только HTTPS.

Случаи использования геолокации по IP-адресу: когда требуется определить местоположение без запроса разрешения пользователя, например, для выбора валюты по умолчанию. Геолокация по IP-адресу работает незаметно в фоновом режиме. Geolocation API требует явного согласия пользователя, что означает запрос разрешения. В любом случае, когда важна точность и пользователь готов поделиться информацией, использование нативного API будет лучшим решением.

Заключение

Я не призываю вас к удалению вашего package.json. Чаще всего, сторонние библиотеки существуют по разумным причинам: лучший опыт разработки, обработка крайних случаев, интеграция с экосистемой, ускорение итераций. Во многих случаях они остаются правильным выбором.

Цель проста - изменить рефлекс. Перед выполнением npm install проверьте, не предоставляют ли современные Web API требуемый вам функционал. Браузеры сейчас предоставляют нативные решения для многих задач, которые раньше решались с помощью зависимостей, от обработки данных до управления производительностью и выполнении задач в фоновом режиме.

При аудите зависимостей, уменьшении размера сборки или модернизации архитектуры клиентского приложения начините с простого вопроса: поддерживает ли это браузер? Включение этой проверки в рабочий процесс может привести к уменьшению размера сборки, снижению рисков обслуживания и созданию более отказоустойчивой кодовой базы, находящейся ближе к самой платформе.