一般的なパターン マッチングのユースケースを標準化するためのアプローチです。
背景
ルーティングは、すべてのウェブ アプリケーションの重要な要素です。ルーティングの核心部分では、URL を取得し、パターン マッチングやその他のアプリ固有のロジックを適用してから、通常は結果に基づいてウェブ コンテンツを表示します。ルーティングはさまざまな方法で実装できます。ディスク上のファイルへのパスをマッピングするサーバーで実行されるコードや、現在の場所の変更を待機して対応する DOM の一部を作成するシングルページ アプリ内のロジックなどがあります。
明確な基準はありませんが、ウェブ デベロッパーは、URL ルーティング パターンを表現するための共通の構文を支持しています。この構文は、regular expressions
と多くの共通点がありますが、パスセグメントをマッチングするためのトークンなどのドメイン固有の追加もいくつかあります。Express や Ruby on Rails などの一般的なサーバーサイド フレームワークでは、この構文(またはそれに非常に近いもの)が使用されています。JavaScript デベロッパーは path-to-regexp
や regexpparam
などのモジュールを使用して、そのロジックを独自のコードに追加できます。
URLPattern
は、これらのフレームワークで作成された基盤の上に構築されたウェブ プラットフォームに追加されたものです。その目的は、ワイルドカード、名前付きトークン グループ、正規表現グループ、グループ修飾子のサポートなど、ルーティング パターン構文を標準化することです。この構文で作成された URLPattern
インスタンスは、完全な URL または URL pathname
と照合し、トークンとグループの一致に関する情報を返すなど、一般的なルーティング タスクを実行できます。
ウェブ プラットフォームで直接 URL マッチングを提供するもう一つの利点は、URL との照合も必要とする他の API と一般的な構文を共有できることです。
ブラウザ サポートとポリフィル
Chrome と Edge バージョン 95 以降では、URLPattern
がデフォルトで有効になっています。
urlpattern-polyfill
ライブラリは、組み込みサポートがないブラウザや Node などの環境で URLPattern
インターフェースを使用する方法を提供します。ポリフィルを使用する場合は、現在の環境でサポートされていない場合にのみ読み込むように、特徴検出を使用してください。そうしないと、URLPattern
の主なメリット(サポート環境を使用するために追加のコードをダウンロードして解析する必要がないという)が失われます。
if (!(globalThis && 'URLPattern' in globalThis)) {
// URLPattern is not available, so the polyfill is needed.
}
構文の互換性
URLPattern
の指針の 1 つは、改革を避けることです。Express や Ruby on Rails で使用されるルーティング構文に精通している場合は、新たに学ぶ必要はありません。ただし、一般的なルーティング ライブラリでは構文が若干異なるため、基本構文として何かを選択する必要がありました。URLPattern
の設計者は、(API サーフェスではなく)path-to-regexp
のパターン構文を出発点として使用することにしました。
この決定は、path-to-regexp
の現在のメンテナンス担当者と緊密に協議したうえで行われました。
サポートされている構文のコアを理解するには、path-to-regexp
のドキュメントをご覧ください。MDN での公開は、GitHub の現在のホームにあるドキュメントをご覧ください。
その他の機能
URLPattern
の構文は、path-to-regexp
がサポートする機能のスーパーセットです。URLPattern
は、ホスト名のワイルドカードを含むオリジンの照合という、ルーティング ライブラリでは一般的ではない機能をサポートしています。他のほとんどのルーティング ライブラリでは、パス名のみを処理し、場合によっては URL の検索部分やハッシュ部分も処理します。これらは自己完結型のウェブアプリ内の同一オリジン ルーティングにのみ使用されるため、URL のオリジン部分をチェックする必要はありません。
オリジンを考慮することで、Service Worker の 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',
});
また、オリジンに関するすべての情報を 1 つのプロパティ 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'});
コンストラクタにオブジェクトを渡す代わりに、1 つまたは 2 つの文字列を指定できます。文字列を 1 つ指定する場合は、オリジンの一致に使用されるパターン情報を含む完全な URL パターンを表す必要があります。2 つの文字列を指定した場合、2 番目の文字列は baseURL
として使用され、最初の文字列はそのベースを基準とする相対値とみなされます。
文字列が 1 つでも 2 つでも、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:blankshould be escaped as
'about\:blank'` で文字列として指定するとエスケープします。
パターンの使用
作成した URLPattern
を使用するには、2 つの方法があります。test()
メソッドと exec()
メソッドは同じ入力を受け取り、同じアルゴリズムを使用して一致を確認します。戻り値が異なるだけです。test()
は、指定された入力に一致するものがあれば true
を返し、一致しない場合は false
を返します。exec()
は、一致に関する詳細情報をキャプチャ グループとともに返します。一致しない場合は null
を返します。次の例では exec()
を使用していますが、単純なブール値の戻り値のみが必要な場合は、test()
に置き換えることもできます。
test()
メソッドと exec()
メソッドを使用する方法の一つは、文字列を渡すことです。コンストラクタがサポートするものと同様に、単一の文字列を指定する場合は、オリジンを含む完全な URL にする必要があります。2 つの文字列が指定されている場合、2 番目の文字列は 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.
ワイルドカードまたはトークンを含む URLPattern
で exec()
を使用すると、戻り値から、入力 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
など)があります。したがって、グループが URLPattern
の pathname
部分の一部として定義されている場合、一致は戻り値の pathname.groups
で見つけることができます。一致は、対応するパターンが匿名グループと名前付きグループのどちらであるかに応じて異なります。
配列インデックスを使用して、匿名パターン一致の値にアクセスできます。匿名パターンが複数ある場合は、インデックス 0
が左端のパターンに一致する値を表し、1
以降のインデックスが後続のパターンに使用されます。
名前付きグループをパターンで使用する場合、一致は、各グループ名に対応する名前を持つプロパティとして公開されます。
Unicode のサポートと正規化
URLPattern
は、いくつかの方法で Unicode 文字をサポートしています。
:café
などの名前付きグループには Unicode 文字を含めることができます。名前付きグループには、有効な JavaScript 識別子に使用されるルールが適用されます。パターン内のテキストは、その特定のコンポーネントの URL エンコードに使用されるのと同じルールに従って、自動的にエンコードされます。
pathname
内の Unicode 文字はパーセント エンコードされるため、/café
などのpathname
パターンは自動的に/caf%C3%A9
に正規化されます。hostname
内の Unicode 文字は、パーセント エンコードではなく、Punycode を使用して自動的にエンコードされます。正規表現グループには ASCII 文字のみを含める必要があります。正規表現の構文により、これらのグループ内の Unicode 文字を自動的にエンコードすることは難しく、安全ではありません。正規表現グループ内の Unicode 文字と一致する場合は、
café
と一致するように(caf%C3%A9)
のようにパーセント エンコードを手動で行う必要があります。
URLPattern
は、Unicode 文字のエンコードに加えて、URL の正規化も行います。たとえば、pathname
コンポーネントの /foo/./bar
は、同等の /foo/bar
に折りたたまれています。
特定の入力パターンがどのように正規化されたか不明な場合は、作成された URLPattern
インスタンスをブラウザの DevTools を使用して検査します。
すべてを組み合わせる
以下に埋め込まれた Glitch デモは、Service Worker の fetch event handler
内にある URLPattern
の主要なユースケースを示しており、ネットワーク リクエストに対するレスポンスを生成できる非同期関数に特定のパターンをマッピングしています。この例のコンセプトは、サーバーサイドまたはクライアントサイドの他のルーティング シナリオにも適用できます。
フィードバックと今後の計画
URLPattern
の基本機能は Chrome と Edge に実装されていますが、今後追加される予定です。URLPattern
の一部はまだ開発中であり、特定の動作に関する未解決の問題が多数あり、これらについてはまだ改善の余地があります。URLPattern
をお試しいただき、GitHub の問題からフィードバックをお寄せください。
テンプレートのサポート
path-to-regexp
ライブラリには、ルーティングの動作を実質的に逆転する compile() function
が用意されています。compile()
は、トークン プレースホルダのパターンと値を受け取り、それらの値を置き換えた URL パスの文字列を返します。
将来的には URLPattern に追加したいと考えていますが、初期リリースの対象外です。
将来のウェブ プラットフォーム機能の有効化
URLPattern
がウェブ プラットフォームの定着部分になると、ルーティングやパターン マッチングの恩恵を受ける可能性のある他の機能を、その上にプリミティブとして構築できます。
Service Worker のスコープ パターン マッチング、ファイル ハンドラとしての PWA、投機的プリフェッチなど、提案された機能に対する URLPattern
の使用については現在議論されています。
謝辞
謝辞の完全なリストについては、元の説明ドキュメントをご覧ください。