URLPattern переносит маршрутизацию на веб-платформу

Опубликовано: 22 июля 2021 г.

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

  • Серверный код, сопоставляющий пути к файлам на диске.
  • Логика в одностраничном приложении, которая ожидает изменений текущего местоположения, а затем создает и отображает соответствующий фрагмент DOM.

Хотя единого общепринятого стандарта не существует, веб-разработчики склоняются к общему синтаксису для выражения шаблонов маршрутизации URL-адресов, который во многом схож с regular expressions , но с некоторыми специфическими для предметной области дополнениями, такими как токены для сопоставления сегментов пути. Популярные серверные фреймворки, такие как Express и Ruby on Rails, используют этот синтаксис (или нечто очень близкое к нему), а разработчики JavaScript могут использовать модули, такие как path-to-regexp или regexpparam чтобы добавить эту логику в свой собственный код.

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

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

Поддержка браузеров и полифилы

URLPattern включена по умолчанию в Chrome и Edge версии 95 и выше.

Библиотека urlpattern-polyfill предоставляет способ использования интерфейса URLPattern в браузерах или средах, таких как Node , которые не имеют встроенной поддержки. Если вы используете полифил, убедитесь, что вы используете определение функций, чтобы гарантировать, что он загружается только в том случае, если текущая среда не поддерживает этот интерфейс. В противном случае вы потеряете одно из ключевых преимуществ URLPattern : тот факт, что средам с поддержкой не нужно загружать и анализировать дополнительный код для его использования.

if (!(globalThis && 'URLPattern' in globalThis)) {
  // URLPattern is not available, so the polyfill is needed.
}

Синтаксическая совместимость

Основной принцип работы URLPattern — избегать повторного изобретения. Если вы уже знакомы с синтаксисом маршрутизации, используемым в Express или Ruby on Rails, вам не нужно изучать ничего нового. Но, учитывая небольшие расхождения в синтаксисе популярных библиотек маршрутизации, необходимо было выбрать базовый синтаксис, и разработчики URLPattern решили использовать синтаксис шаблонов из path-to-regexp (хотя и не его API) в качестве отправной точки.

Это решение было принято после тщательных консультаций с нынешним сопровождающим проекта path-to-regexp .

Лучший способ ознакомиться с основными принципами поддерживаемого синтаксиса — обратиться к документации по path-to-regexp . Вы можете прочитать документацию, предназначенную для публикации на MDN, в её текущем местоположении на GitHub.

Дополнительные функции

Синтаксис URLPattern является расширенным набором функций, поддерживаемых path-to-regexp , поскольку URLPattern поддерживает редкую для библиотек маршрутизации функцию: сопоставление источников , включая подстановочные знаки в именах хостов. Большинство других библиотек маршрутизации работают только с путем к URL-адресу , а иногда и с частью поиска или хеша . Им никогда не приходится проверять часть URL-адреса, относящуюся к источнику, поскольку они используются только для маршрутизации внутри автономного веб-приложения.

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

Примеры

Построение узора

Для создания объекта URLPattern передайте его конструктору либо строки, либо объект, свойства которого содержат информацию о шаблоне, с которым нужно выполнить сопоставление.

Передача объекта обеспечивает наиболее явный контроль над тем, какой шаблон использовать для сопоставления каждого компонента URL. В самом многословном виде это может выглядеть так:

const p = new URLPattern({
  protocol: 'https',
  username: '',
  password: '',
  hostname: 'example.com',
  port: '',
  pathname: '/foo/:image.jpg',
  search: '*',
  hash: '*',
});

Указание пустой строки для свойства будет соответствовать условию только в том случае, если соответствующая часть URL-адреса не задана. Символ подстановки * будет соответствовать любому значению для заданной части URL-адреса.

Конструктор предлагает несколько упрощений для более удобного использования. Полное отсутствие свойств search и hash , или любых других, эквивалентно установке для них символа подстановки '*' . Пример можно упростить до:

const p = new URLPattern({
  protocol: 'https',
  username: '',
  password: '',
  hostname: 'example.com',
  port: '',
  pathname: '/foo/:image.jpg',
});

В качестве дополнительного упрощения, всю информацию об источнике можно указать в одном свойстве, baseURL , что приведет к следующему результату:

const p = new URLPattern({
  pathname: '/foo/:image.jpg',
  baseURL: 'https://example.com',
});

Все эти примеры предполагают, что в вашем случае требуется сопоставление источников. Если вас интересует сопоставление только по остальным частям URL-адреса, исключая источник (как это часто бывает в сценариях маршрутизации с одним источником), вы можете полностью опустить информацию об источнике и указать лишь комбинацию свойств pathname , search и hash . Как и прежде, опущенные свойства будут рассматриваться как заданные с помощью шаблона * .

const p = new URLPattern({pathname: '/foo/:image.jpg'});

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

Независимо от того, предоставлена ​​ли одна или две строки, конструктор URLPattern проанализирует полный шаблон URL, разбив его на компоненты URL, и сопоставит каждую часть большего шаблона с соответствующим компонентом. Это означает, что на практике каждый URLPattern , созданный со строками, представляется так же, как и эквивалентный URLPattern созданный с помощью объекта. Конструктор со строками — это просто сокращение для тех, кто предпочитает менее многословный интерфейс.

const p = new URLPattern('https://example.com/foo/:image.jpg?*#*');

При использовании строк для создания URLPattern следует учитывать несколько нюансов.

Пропуск свойства при использовании объекта для построения URLPattern эквивалентен указанию символа подстановки * для этого свойства. При разборе полного строкового шаблона URL, если в одном из компонентов URL отсутствует значение, это рассматривается так, как если бы свойство компонента было установлено в значение '' , что будет соответствовать только в том случае, если этот компонент пуст.

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

// p1 and p2 are equivalent.
const p1 = new URLPattern('/foo', location.origin);
const p2 = new URLPattern({
  protocol: location.protocol,
  hostname: location.hostname,
  pathname: '/foo',
  search: '',
  hash: '',
});

// p3 and p4 are equivalent.
const p3 = new URLPattern('/foo?*#*', location.origin);
const p4 = new URLPattern({
  protocol: location.protocol,
  hostname: location.hostname,
  pathname: '/foo',
});

Также следует помнить, что разбор строкового шаблона на его компоненты может быть неоднозначным. Существуют символы, такие как : `, которые встречаются в URL-адресах, но также имеют особое значение в синтаксисе сопоставления шаблонов. Чтобы избежать этой неоднозначности, конструктор URLPattern предполагает, что любой из этих специальных символов является частью шаблона, а не частью URL-адреса. Если вы хотите, чтобы неоднозначный символ интерпретировался как часть URL-адреса, обязательно экранируйте его \` character. For example, the literal URL about:blank` should be escaped as `'about\:blank'`, если он предоставлен в виде строки.

Используйте шаблон

После создания объекта URLPattern у вас есть два варианта его использования. Методы test() и exec() принимают одни и те же входные данные и используют один и тот же алгоритм для проверки совпадения, отличаясь только возвращаемым значением. test() возвращает true если найдено совпадение с заданными входными данными, и false в противном случае. exec() возвращает подробную информацию о совпадении вместе с группами захвата или null , если совпадения нет. Следующие примеры демонстрируют использование exec() , но вы можете заменить любой из них на test() если вам нужно только логическое возвращаемое значение.

Один из способов использования методов test() и exec() — передача строк. Аналогично тому, что поддерживает конструктор, если предоставлена ​​одна строка, она должна представлять собой полный URL-адрес, включая источник. Если предоставлены две строки, вторая строка рассматривается как значение baseURL , а первая строка оценивается как относительная к этому базовому значению.

const p = new URLPattern({
  pathname: '/foo/:image.jpg',
  baseURL: 'https://example.com',
});

const result = p.exec('https://example.com/foo/cat.jpg');
// result will contain info about the successful match.
// const result = p.exec('/foo/cat.jpg', 'https://example.com')
// is equivalent, using the baseURL syntax.

const noMatchResult = p.exec('https://example.com/bar');
// noMatchResult will be null.

В качестве альтернативы вы можете передать объект того же типа, что и в конструкторе, со свойствами, которые устанавливают только те части URL-адреса, которые вас интересуют.

const p = new URLPattern({pathname: '/foo/:image.jpg'});

const result = p.exec({pathname: '/foo/:image.jpg'});
// result will contain info about the successful match.

При использовании exec() для URLPattern , содержащего подстановочные знаки или токены, возвращаемое значение будет содержать информацию о соответствующих значениях во входном URL. Это избавит вас от необходимости самостоятельно извлекать эти значения.

const p = new URLPattern({
  hostname: ':subdomain.example.com',
  pathname: '/*/:image.jpg'
});

const result = p.exec('https://imagecdn1.example.com/foo/cat.jpg');
// result.hostname.groups.subdomain will be 'imagecdn1'
// result.pathname.groups[0] will be 'foo', corresponding to *
// result.pathname.groups.image will be 'cat'

Анонимные и именные группы

При передаче строки URL в функцию exec() вы получаете значение, указывающее, какие части соответствуют всем группам шаблона.

Возвращаемое значение имеет свойства, соответствующие компонентам URLPattern , например, pathname . Таким образом, если группа была определена как часть pathname в URLPattern , то совпадения можно найти в pathname.groups возвращаемого значения. Совпадения отображаются по-разному в зависимости от того, является ли соответствующий шаблон анонимной или именованной группой.

Для доступа к значениям анонимного шаблона можно использовать индексы массива. Если анонимных шаблонов несколько, индекс 0 представляет собой значение, соответствующее самому левому шаблону, а индексы 1 и далее используются для последующих шаблонов.

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

Поддержка и нормализация Unicode

URLPattern поддерживает символы Unicode несколькими способами.

  • Именованные группы, например :café , могут содержать символы Юникода. К именованным группам применяются правила, используемые для допустимых идентификаторов JavaScript .

  • Текст внутри шаблона будет автоматически кодироваться в соответствии с теми же правилами, которые используются для кодирования URL-адресов данного компонента. Символы Unicode в pathname будут кодироваться с помощью процентного кодирования , поэтому шаблон pathname например, /café автоматически нормализуется до /caf%C3%A9 . Символы Unicode в hostname автоматически кодируются с использованием Punycode , а не процентного кодирования.

  • Группы регулярных выражений должны содержать только символы ASCII. Синтаксис регулярных выражений затрудняет и делает небезопасным автоматическое кодирование символов Unicode в этих группах. Если вы хотите сопоставить символ Unicode в группе регулярных выражений, вам необходимо вручную закодировать его с помощью процента, например (caf%C3%A9) для сопоставления с café .

Помимо кодирования символов Unicode, URLPattern также выполняет нормализацию URL-адресов. Например, /foo/./bar в компоненте pathname преобразуется в эквивалентный /foo/bar .

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

Соберите всё воедино

Демонстрация Glitch иллюстрирует ключевой вариант использования URLPattern в fetch event handler сервис-воркера, сопоставляя определенные шаблоны с асинхронными функциями, которые могут генерировать ответ на сетевые запросы. Концепции, представленные в этом примере, могут быть применены и к другим сценариям маршрутизации, как на стороне сервера, так и на стороне клиента.

Отзывы и планы на будущее

Хотя базовый функционал URLPattern уже реализован в Chrome и Edge, планируется его дальнейшее расширение. Некоторые аспекты URLPattern всё ещё находятся в разработке , и существует ряд открытых вопросов относительно конкретных функций, которые могут быть доработаны. Мы рекомендуем вам попробовать URLPattern и оставить свой отзыв через GitHub Issues .

Поддержка шаблонизации

Библиотека path-to-regexp предоставляет compile() function , которая фактически обращает вспять поведение маршрутизации. compile() принимает шаблон и значения для заполнителей токенов и возвращает строку с URL-путем, в который подставлены эти значения.

Мы надеемся добавить это в URLPattern в будущем, но это не входит в планы первоначального релиза.

Обеспечить поддержку будущих функций веб-платформы

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

В настоящее время ведутся дискуссии об использовании URLPattern для предлагаемых функций, таких как сопоставление шаблонов области действия сервис-воркера , использование PWA в качестве обработчиков файлов и спекулятивная предварительная загрузка .

Полный список благодарностей см. в оригинальном пояснительном документе .