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, вам не придется изучать что-то новое. But given the slight divergences between syntaxes in popular routing libraries, something had to be chosen as the base syntax, and the designers of URLPattern decided to use the pattern syntax from path-to-regexp (though not its API surface) as the starting point .

Это решение было принято после тесных консультаций с нынешним сопровождающим 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'

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

When you pass a URL string to exec() , you get back a value telling your which portions matched all of the pattern's groups.

Возвращаемое значение имеет свойства, соответствующие компонентам 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 в качестве обработчиков файлов и спекулятивная предварительная выборка .

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

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