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

Подход к стандартизации общих вариантов использования сопоставления с образцом.

Фон

Маршрутизация является ключевой частью каждого веб-приложения. По своей сути маршрутизация включает в себя получение 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 поддерживает необычную функцию среди библиотек маршрутизации: сопоставление источников , включая подстановочные знаки в именах хостов. Большинство других библиотек маршрутизации имеют дело только с pathname и иногда с поисковой или хэш -частью 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 , а первая строка считается относительно этой базы.

Независимо от того, предоставлена ​​ли одна строка или две, конструктор 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 поддерживает символы Юникода несколькими различными способами.

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

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

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

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

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

Собираем все это вместе

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

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

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

Поддержка шаблонов

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

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

Включение будущих функций веб-платформы

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

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

Благодарности

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