Menu

URL и URLSearchParams в JavaScript: парсинг URL

Разбираем, как парсить, собирать и менять URL в JavaScript через URL и URLSearchParams — без регулярок и без подводных камней с кодированием.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Хватит парсить URL строковыми трюками

До появления API URL все резали адреса через split('?'), регулярки и молитвы. Работало — ровно до того момента, пока в значении не появлялся &, =, пробел или какой-нибудь не-ASCII символ. И тут всё разваливалось. И в браузере, и в Node есть нормальный парсер. Пользуйтесь им.

Один вызов — и весь URL уже разобран на части и корректно декодирован. При невалидном вводе конструктор бросает TypeError, и это как раз то, что нужно в большинстве случаев: мусорный URL должен падать громко, а не тихо протаскивать мусор дальше по коду.

Как получить query-параметры из URL

У каждого объекта URL есть свойство .searchParams — это объект URLSearchParams, который умеет читать и изменять query string:

Несколько моментов, на которые стоит обратить внимание:

  • Значения возвращаются уже декодированными. ?name=Ada%20Lovelace превратится в "Ada Lovelace".
  • Всё — это строка. "2" — это не 2. Если нужно число, преобразуйте через Number().
  • Повторяющиеся ключи — это нормально. get вернёт первое совпадение, а getAll — все.
  • Для отсутствующих ключей возвращается null, а не undefined — так что ?? "default" отлично с ними работает.

Как собрать query string в JavaScript

Собрать query-строку с нуля можно через URLSearchParams — без ручного экранирования и склейки через &:

А ещё его можно собрать из объекта — подойдёт любой итерируемый набор пар [ключ, значение], да и обычный объект тоже сгодится:

set и append: set перезаписывает уже существующее значение по этому ключу, а append добавляет ещё одно. append удобно использовать, когда ключ может законно повторяться (теги, фильтры); set — для параметров с единственным значением.

Как изменить URL

Объект URL живой, поэтому любые изменения в searchParams автоматически отражаются в .search и .href:

Это идиоматичный способ добавить query-параметр к уже существующему URL. Не нужно проверять, есть ли в URL знак ?, и ломать голову над тем, ставить & или ?.

Точно так же можно менять и другие части URL:

Перебор параметров

URLSearchParams — это итерируемый объект. for...of выдаёт пары [key, value], а ещё есть привычные помощники keys(), values() и entries():

Учти, что повторяющиеся ключи попадают в итерацию отдельными записями — сначала увидишь tag = web, затем tag = beginner. Это честное отражение того, что реально лежит в query-строке.

Если нужно по-быстрому вывести всё как обычный объект для отладки, подойдёт Object.fromEntries — правда, он схлопывает дубликаты и оставляет только последнее значение:

Пригодится для отладки. Но если какой-то ключ может повторяться — это уже неверный подход.

Относительные URL требуют базы

Вызов new URL("/search?q=js") сам по себе выбросит исключение — относительный путь без базы не является валидным URL. Передайте базовый адрес вторым аргументом:

Правила разрешения здесь те же, что браузер применяет к <a href>: ведущий / — это абсолютный путь от хоста, без слеша — относительно текущего пути, .. поднимается на уровень выше. Очень удобно, когда вы собираете URL к API из заранее заданной базы.

В браузере window.location.href — это готовая база для парсинга URL текущей страницы:

const u = new URL(window.location.href);
const page = u.searchParams.get("page") ?? "1";

Обработка невалидных URL

Конструктор URL бросает исключение на некорректных данных. С одной стороны, это удобно, с другой — значит, что при парсинге URL в JavaScript нужно оборачивать в try/catch всё, что ввёл пользователь или прислала внешняя система:

В современных средах также доступен метод URL.canParse(input) — булева проверка, которая избавляет от плясок с try/catch, когда нужно просто провалидировать URL:

Небольшой рабочий пример

Соберём всё вместе: прочитаем текущие фильтры из URL, немного их подправим и получим новый URL, на который можно перейти:

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

Что стоит запомнить

  • new URL(string) разбирает URL на именованные части. На мусоре кидает исключение.
  • url.searchParams — это URLSearchParams, у него есть get, getAll, set, append, delete, has.
  • Кодирование происходит автоматически. Не лезьте за encodeURIComponent, если только не собираете строку руками.
  • Передавайте базовый URL вторым аргументом, чтобы разрешить относительные пути.
  • URL.canParse (или try/catch) — ваш инструмент валидации для недоверенного ввода.

Каждый раз, когда рука тянется разбить URL через .split('?') или выдернуть query-параметр регуляркой, берите вместо этого встроенные API. Код короче, работает корректно, и всё это уже есть в рантайме.

Часто задаваемые вопросы

Как распарсить URL в JavaScript?

Передайте строку в конструктор URL: const u = new URL('https://example.com/path?x=1'). У полученного объекта есть свойства protocol, host, pathname, search, hash и удобный searchParams. На невалидной строке конструктор кидает исключение, так что при разборе внешних данных оборачивайте вызов в try/catch.

Как получить значение query-параметра?

Через url.searchParams.get('name') — вернётся уже декодированное значение или null, если параметра нет. Если параметр может повторяться (?tag=a&tag=b), используйте searchParams.getAll('tag') — получите массив всех значений.

Чем URL отличается от URLSearchParams?

URL описывает весь адрес целиком — протокол, хост, путь, query, хэш. URLSearchParams отвечает только за query-строку и может использоваться отдельно, чтобы собрать или разобрать что-то вроде a=1&b=2. При этом у каждого экземпляра URL есть свойство .searchParams — это как раз URLSearchParams, привязанный к этому URL.

Нужно ли кодировать параметры вручную?

Нет. URLSearchParams сам кодирует ключи и значения при вызове set, append и при сериализации в строку. Пробелы, &, =, Unicode — всё обрабатывается корректно. encodeURIComponent имеет смысл только если вы зачем-то собираете URL руками по кусочкам, а этого лучше не делать.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ