一般的なパターン マッチングのユースケースを標準化するアプローチ。
背景
ルーティングは、すべてのウェブ アプリケーションの重要な要素です。ルーティングの基本は、URL を取得し、パターン マッチングなどのアプリ固有のロジックを適用し、通常は結果に基づいてウェブ コンテンツを表示することです。ルーティングはさまざまな方法で実装できます。たとえば、ディスク上のファイルにパスをマッピングするサーバーで実行されるコードや、現在の位置の変更を待機して、表示する DOM の対応する部分を作成するシングルページ アプリのロジックなどです。
明確な標準はありませんが、ウェブ デベロッパーは、regular expressions
と多くの共通点がある URL ルーティング パターンを表現するための共通構文に傾倒しています。ただし、パスセグメントを照合するためのトークンなど、ドメイン固有の追加機能もあります。Express や Ruby on Rails などの一般的なサーバーサイド フレームワークでは、この構文(またはそれに非常に近い構文)が使用されます。JavaScript デベロッパーは、path-to-regexp
や regexpparam
などのモジュールを使用して、そのロジックを独自のコードに追加できます。
URLPattern
は、これらのフレームワークによって作成された基盤上に構築されたウェブ プラットフォームの追加機能です。ワイルドカード、名前付きトークン グループ、正規表現グループ、グループ修飾子のサポートなど、ルーティング パターンの構文を標準化することを目的としています。この構文で作成された URLPattern
インスタンスは、完全な URL または URL pathname
との照合や、トークンとグループの一致に関する情報の返却など、一般的なルーティング タスクを実行できます。
ウェブ プラットフォームで URL マッチングを直接提供するもう 1 つのメリットは、URL との照合も必要な他の API と共通の構文を共有できることです。
ブラウザのサポートとポリフィル
URLPattern
は、Chrome と Edge のバージョン 95 以降ではデフォルトで有効になっています。
urlpattern-polyfill
ライブラリを使用すると、組み込みのサポートがないブラウザや Node などの環境で URLPattern
インターフェースを使用できます。ポリフィルを使用する場合は、機能検出を使用して、現在の環境でサポートされていない場合にのみ読み込むようにしてください。そうしないと、URLPattern
の重要な利点の 1 つである、サポート環境で使用するために追加のコードをダウンロードして解析する必要がないことが失われます。
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
は、ホスト名のワイルドカードを含む送信元の照合など、ルーティング ライブラリでは珍しい機能をサポートしているため、URLPattern
の構文は path-to-regexp
のサポート範囲のスーパーセットです。他のほとんどのルーティング ライブラリは、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',
});
追加のショートカットとして、送信元に関するすべての情報を 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 つ目の文字列はそのベースに対して相対的と見なされます。
文字列が 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()
メソッドを使用する方法の一つは、文字列を渡すことです。コンストラクタがサポートするものと同様に、1 つの文字列を指定する場合は、オリジンを含む完全な 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
に圧縮されます。
特定の入力パターンがどのように正規化されたか不明な場合は、ブラウザの DevTools を使用して、作成された URLPattern
インスタンスを調べます。
すべてを組み合わせる
以下に埋め込まれた Glitch デモは、サービス ワーカーの fetch event handler
内の URLPattern
のコア ユースケースを示しています。特定のパターンを、ネットワーク リクエストにレスポンスを生成できる非同期関数にマッピングします。この例のコンセプトは、サーバーサイドまたはクライアントサイドの他のルーティング シナリオにも適用できます。
フィードバックと今後の計画
URLPattern
の基本機能は Chrome と Edge に実装されていますが、追加機能の追加が予定されています。URLPattern
の一部の機能は現在開発中であり、特定の動作についてはまだ改良の余地があるため、未解決の問題がいくつかあります。URLPattern
をお試しいただき、GitHub の問題を通じてフィードバックをお寄せください。
テンプレートのサポート
path-to-regexp
ライブラリには、ルーティング動作を効果的に逆転する compile() function
が用意されています。compile()
は、トークン プレースホルダのパターンと値を取り、それらの値が挿入された URL パスの文字列を返します。
今後、URLPattern に追加する予定ですが、最初のリリースの範囲外です。
今後のウェブ プラットフォームの機能を有効にする
URLPattern
がウェブ プラットフォームの確立された部分になると仮定すると、ルーティングやパターン マッチングから利益を得られる他の機能は、プリミティブとしてその上に構築できます。
サービス ワーカー スコープ パターン マッチング、ファイル ハンドラとしての PWA、推測プリフェッチなどの提案されている機能に URLPattern
を使用するかどうかについて、現在も議論が続いています。
謝辞
謝辞の一覧については、元の説明ドキュメントをご覧ください。