URLPattern aporta el enrutamiento a la plataforma web

Un enfoque para estandarizar casos de uso comunes de coincidencia de patrones.

Información general

El enrutamiento es una pieza clave de todas las aplicaciones web. En esencia, el enrutamiento implica tomar una URL, aplicarle alguna coincidencia de patrones o alguna otra lógica específica de la app y, luego, por lo general, mostrar contenido web basado en el resultado. El enrutamiento se puede implementar de varias maneras: a veces, a través de un código que se ejecuta en un servidor que asigna una ruta de acceso a los archivos del disco, o mediante la lógica en una app de una sola página que espera los cambios en la ubicación actual y crea una parte del DOM correspondiente para mostrar.

Si bien no existe un estándar definitivo, los desarrolladores web se inclinaron hacia una sintaxis común para expresar patrones de enrutamiento de URL que tienen mucho en común con regular expressions, pero con algunas adiciones específicas del dominio, como tokens para segmentos de ruta de acceso coincidentes. Los populares frameworks del servidor, como Express y Ruby on Rails, usan esta sintaxis (o algo muy parecido), y los desarrolladores de JavaScript pueden usar módulos como path-to-regexp o regexpparam para agregar esa lógica a su propio código.

URLPattern es una adición a la plataforma web que se basa en la base creada por estos frameworks. Su objetivo es estandarizar una sintaxis de patrón de enrutamiento, incluida la compatibilidad con comodines, grupos de tokens con nombre, grupos de expresiones regulares y modificadores de grupo. Las instancias de URLPattern creadas con esta sintaxis pueden realizar tareas de enrutamiento comunes, como establecer coincidencias con URLs completas o una URL pathname, y mostrar información sobre las coincidencias del token y el grupo.

Otro beneficio de proporcionar coincidencias de URL directamente en la plataforma web es que una sintaxis común se puede compartir con otras APIs que también deben coincidir con las URLs.

Compatibilidad con navegadores y polyfills

URLPattern está habilitado de forma predeterminada en Chrome y Edge 95 y versiones posteriores.

La biblioteca de urlpattern-polyfill proporciona una forma de usar la interfaz URLPattern en navegadores o entornos como Node, que no tienen compatibilidad integrada. Si usas el polyfill, asegúrate de usar la detección de funciones para asegurarte de cargarlo solamente si el entorno actual no es compatible. De lo contrario, perderás uno de los beneficios clave de URLPattern: el hecho de que los entornos de compatibilidad no necesitan descargar ni analizar código adicional para usarlo.

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

Compatibilidad de la sintaxis

Una filosofía guía para URLPattern es evitar la reinvención. Si ya conoces la sintaxis de enrutamiento que se usa en Express o Ruby on Rails, no deberías tener que aprender nada nuevo. Sin embargo, dadas las leves divergencias entre las sintaxis en las bibliotecas de enrutamiento populares, se tuvo que elegir algo como la sintaxis base, y los diseñadores de URLPattern decidieron usar la sintaxis de patrones de path-to-regexp (aunque no su superficie de API) como punto de partida.

Esta decisión se tomó después de una estrecha consulta con el encargado de mantenimiento actual de path-to-regexp.

La mejor manera de familiarizarte con el núcleo de la sintaxis admitida es consultar la documentación sobre path-to-regexp. Puedes leer la documentación destinada a su publicación en MDN en la página principal actual de GitHub.

Características adicionales

La sintaxis de URLPattern es un superconjunto de lo que admite path-to-regexp, ya que URLPattern admite una función poco común entre las bibliotecas de enrutamiento: coincidencias de orígenes, incluidos comodines en nombres de host. La mayoría de las otras bibliotecas de enrutamiento solo tratan con pathname y, en ocasiones, con la parte de búsqueda o hash de una URL. Nunca tienen que verificar la parte de origen de una URL, ya que solo se usan para el enrutamiento del mismo origen dentro de una app web independiente.

Tener en cuenta los orígenes abre la puerta a casos de uso adicionales, como el enrutamiento de solicitudes de origen cruzado dentro del controlador de eventos fetch de un service worker. Si solo enrutas URLs de mismo origen, puedes ignorar esta función adicional y usar URLPattern como otras bibliotecas.

Ejemplos

Construir el patrón

Para crear un URLPattern, pasa a su constructor las cadenas o un objeto cuyas propiedades contengan información sobre el patrón con el que se buscarán coincidencias.

Pasar un objeto ofrece el control más explícito sobre el patrón que se debe usar para hacer coincidir cada componente de la URL. En su expresión más detallada, puede verse así:

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

Proporcionar una cadena vacía para una propiedad solo coincidirá si no se configura la parte correspondiente de la URL. El comodín * coincidirá con cualquier valor para una parte determinada de la URL.

El constructor ofrece varias combinaciones de teclas para un uso más sencillo. Omitir por completo search y hash, o cualquier otra propiedad, equivale a configurarlas en el comodín '*'. El ejemplo anterior se podría simplificar a

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

Como acceso directo adicional, toda la información sobre el origen se puede proporcionar en una sola propiedad, baseURL, lo que lleva a

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

En todos estos ejemplos, se supone que tu caso de uso implica coincidir orígenes. Si solo te interesa establecer coincidencias con las otras partes de la URL, sin incluir el origen (como sucede con muchas situaciones de enrutamiento de origen único "tradicionales"), puedes omitir la información de origen por completo y solo proporcionar alguna combinación de las propiedades pathname, search y hash. Como antes, las propiedades omitidas se tratarán como si se hubieran establecido en el patrón comodín *.

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

Como alternativa a pasar un objeto al constructor, puedes proporcionar una o dos cadenas. Si se proporciona una cadena, debe representar un patrón de URL completo, incluida la información del patrón que se usa para hacer coincidir el origen. Si proporcionas dos strings, la segunda se usa como baseURL, y la primera se considera relacionada con esa base.

Ya sea que se proporcione una o dos strings, el constructor URLPattern analizará el patrón de URL completo, lo dividirá en componentes de URL y asignará cada parte del patrón más grande al componente correspondiente. Esto significa que, de forma interna, cada URLPattern creado con cadenas se representa de la misma manera que un URLPattern equivalente creado con un objeto. El constructor de cadenas es solo un acceso directo para aquellos que prefieren una interfaz menos detallada.

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

Cuando uses cadenas para crear un URLPattern, debes tener en cuenta algunas advertencias.

Dejar una propiedad cuando se usa un objeto para construir URLPattern es equivalente a proporcionar un comodín * para esa propiedad. Cuando se analiza el patrón completo de la cadena de URL, si a uno de los componentes de URL le falta un valor, se trata como si la propiedad del componente se hubiera establecido en '', que solo coincidirá cuando ese componente esté vacío.

Cuando usas strings, debes incluir de forma explícita los comodines si deseas que se usen en el URLPattern construido.

// 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',
});

También debes tener en cuenta que analizar un patrón de cadena en sus componentes puede ser ambiguo. Hay caracteres, como :, que se encuentran en las URLs pero que también tienen un significado especial en la sintaxis de coincidencia de patrones. Para evitar esta ambigüedad, el constructor URLPattern supone que cualquiera de esos caracteres especiales es parte de un patrón, no parte de la URL. Si quieres que se interprete un carácter ambiguo como parte de la URL, asegúrate de escaparlo con un \` character. For example, the literal URLabout:blankshould be escaped as'about\:blank'` cuando se proporcione como una cadena.

Usa el patrón

Después de construir un URLPattern, tienes dos opciones para usarlo. Los métodos test() y exec() toman la misma entrada y usan el mismo algoritmo para verificar una coincidencia, y solo difieren en el valor que se muestra. test() muestra true cuando hay una coincidencia para la entrada dada y false en el caso contrario. exec() muestra información detallada sobre la coincidencia junto con los grupos de captura o null si no hay coincidencia. En los siguientes ejemplos, se demuestra el uso de exec(), pero puedes intercambiar test() por cualquiera de ellos si solo quieres un valor booleano simple que se muestra.

Una forma de usar los métodos test() y exec() es pasar strings. De manera similar a lo que admite el constructor, si se proporciona una sola string, debería ser una URL completa, incluido el origen. Si se proporcionan dos strings, la segunda se trata como un valor de baseURL, y la primera se evalúa como relacionada con esa base.

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.

Como alternativa, puedes pasar el mismo tipo de objeto que admite el constructor, con propiedades configuradas solo para las partes de la URL que te interesan.

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

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

Cuando usas exec() en una URLPattern que contiene comodines o tokens, el valor que se muestra te brinda información sobre cuáles eran los valores correspondientes en la URL de entrada. Esto puede ahorrarte la molestia de tener que analizar esos valores por tu cuenta.

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'

Grupos anónimos y con nombre

Cuando pasas una cadena de URL a exec(), obtienes un valor que indica qué partes coincidieron con todos los grupos del patrón.

El valor que se muestra tiene propiedades que corresponden a los componentes de URLPattern, como pathname. Por lo tanto, si un grupo se definió como parte de la parte pathname de URLPattern, las coincidencias se pueden encontrar en el pathname.groups del valor que se muestra. Las coincidencias se representan de manera diferente según si el patrón correspondiente era un grupo anónimo o con nombre.

Puedes usar índices de array para acceder a los valores de una coincidencia de patrones anónima. Si hay varios patrones anónimos, el índice 0 representará el valor coincidente del más a la izquierda, con 1 y otros índices que se usarán para los patrones posteriores.

Cuando se usan grupos con nombre en un patrón, las coincidencias se exponen como propiedades cuyos nombres corresponden al nombre de cada grupo.

Compatibilidad y normalización de Unicode

URLPattern admite caracteres Unicode de diferentes maneras.

  • Los grupos con nombre, como :café, pueden contener caracteres Unicode. Las reglas que se usan para los identificadores de JavaScript válidos se aplican a los grupos con nombre.

  • El texto dentro de un patrón se codificará automáticamente de acuerdo con las mismas reglas usadas para la codificación de URL de ese componente en particular. Los caracteres Unicode dentro de pathname estarán codificados por porcentaje, por lo que un patrón de pathname como /café se normaliza a /caf%C3%A9 automáticamente. Los caracteres Unicode de hostname se codifican automáticamente con Punycode, en lugar de codificación por porcentajes.

  • Los grupos de expresiones regulares deben contener solo caracteres ASCII. La sintaxis de la expresión regular dificulta y hace que no sea seguro codificar automáticamente los caracteres Unicode en estos grupos. Si quieres hacer coincidir un carácter Unicode en un grupo de expresiones regulares, debes codificarlo en porcentajes de forma manual, como (caf%C3%A9) para que coincida con café.

Además de codificar caracteres Unicode, URLPattern también realiza la normalización de URL. Por ejemplo, /foo/./bar en el componente pathname se contrae a la /foo/bar equivalente.

Si tienes dudas sobre cómo se normalizó un patrón de entrada determinado, inspecciona la instancia URLPattern construida con las DevTools de tu navegador.

Revisión general

En la demostración de Glitch incorporada a continuación, se muestra un caso de uso principal de URLPattern dentro de fetch event handler de un service worker, que asigna patrones específicos a funciones asíncronas que podrían generar una respuesta a solicitudes de red. Los conceptos de este ejemplo también se podrían aplicar a otras situaciones de enrutamiento, ya sea del servidor o del cliente.

Comentarios y planes futuros

Si bien la funcionalidad básica de URLPattern llegó a Chrome y Edge, se planea agregar más. Algunos aspectos de URLPattern aún están en desarrollo y hay una serie de preguntas abiertas sobre comportamientos específicos que aún se pueden definir mejor. Te recomendamos que pruebes URLPattern y envíes comentarios a través de un problema de GitHub.

Compatibilidad con el uso de plantillas

La biblioteca de path-to-regexp proporciona un compile() function que revierte de manera efectiva el comportamiento de enrutamiento. compile() toma un patrón y valores para los marcadores de posición del token y muestra una cadena para una ruta de URL con esos valores sustituidos.

Esperamos agregar esto a URLPattern en el futuro, pero no está dentro del alcance de la versión inicial.

Habilita funciones futuras de la plataforma web

Si suponemos que URLPattern se convierte en una parte establecida de la plataforma web, otras funciones que podrían beneficiarse del enrutamiento o la coincidencia de patrones pueden compilarse como una primitiva.

Hay debates continuos sobre el uso de URLPattern para las funciones propuestas, como la coincidencia de patrones de alcance de service worker, las AWP como controladores de archivos y la carga previa especulativa.

Agradecimientos

Consulte el documento explicativo original para ver una lista completa de las confirmaciones.