URLPattern 提供路徑至網路平台

一種將常見模式比對用途標準化的做法。

背景

路由是每個網頁應用程式的重要元素。在本質上,路由會擷取網址,並套用一些模式比對或其他應用程式專屬邏輯,然後通常會根據結果顯示網頁內容。您可以透過多種方式實作路由:有時是執行在伺服器上的程式碼,用來將路徑對應至磁碟上的檔案;有時則是單頁應用程式中的邏輯,用來等待目前位置的變更,並建立對應的 DOM 片段進行顯示。

雖然沒有明確的標準,但網頁開發人員傾向使用通用的語法來表示網址路徑模式,這些模式與 regular expressions 有許多共同之處,但也加入了一些特定領域的內容,例如用於比對路徑區段的符記。ExpressRuby on Rails 等熱門伺服器端架構都會使用這個語法 (或類似的語法),而 JavaScript 開發人員可以使用 path-to-regexpregexpparam 等模組,在自己的程式碼中加入該邏輯。

URLPattern 是網頁平台的附加元件,可在這些架構建立的基礎上建構。其目標是將路由模式語法標準化,包括支援萬用字元、命名符記群組、規則運算式群組和群組修飾符。使用此語法建立的 URLPattern 例項可執行常見的路由工作,例如比對完整網址或網址 pathname,以及傳回符記和群組比對結果的資訊。

直接在網頁平台中提供網址比對功能的另一個好處,是可以與其他 API 共用通用語法,因為這些 API 也需要比對網址。

瀏覽器支援和 polyfill

根據預設,Chrome 和 Edge 95 以上版本會啟用 URLPattern

urlpattern-polyfill 程式庫提供一種方法,可在瀏覽器或 Node 等缺乏內建支援的環境中使用 URLPattern 介面。如果您使用 polyfill,請務必使用功能偵測功能,確保您只在目前環境缺乏支援時才載入 polyfill。否則,您將失去 URLPattern 的一項重要優點:支援環境不必下載及剖析額外程式碼,才能使用 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說明文件。您可以閱讀說明文件,瞭解在 GitHub 上發布的 MDN 說明文件。

其他功能

URLPattern 的語法是 path-to-regexp 支援的超集,因為 URLPattern 支援轉送程式庫中不常見的功能:比對來源,包括主機名稱中的萬用字元。大多數其他導覽程式庫只處理pathname,偶爾處理網址的搜尋雜湊部分。因為這些函式只會用於自給自足的網站應用程式中,用於同源路由,因此不必檢查網址的來源部分。

考量來源後,您就能使用其他用途,例如在服務工作者fetch 事件處理常式中,路由跨來源要求。如果您只會轉送相同來源的網址,可以有效地忽略這項額外功能,並像其他程式庫一樣使用 URLPattern

範例

建構模式

如要建立 URLPattern,請將其建構函式傳遞給字串或物件,其中屬性包含要比對的模式相關資訊。

傳遞物件可提供最明確的控制權,讓您決定要使用哪個模式來比對每個網址元件。最詳細的格式如下:

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

只有在未設定網址的對應部分時,系統才會為屬性提供空字串。萬用字元 * 會比對網址指定部分的任何值。

建構函式提供多種快速鍵,可簡化使用方式。完全省略 searchhash 或任何其他屬性,等同於將這些屬性設為 '*' 萬用字元。上述範例可簡化為

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

所有這些範例都假設您的用途涉及比對來源。如果您只想比對網址的其他部分,而非來源 (許多「傳統」單來源路由情況即屬此類),則可以完全省略來源資訊,只提供 pathnamesearchhash 屬性的組合。如同先前所述,系統會將省略的屬性視為設為 * 萬用字元模式。

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

您可以提供一或兩個字串,做為傳遞物件至建構函式的替代做法。如果您提供一個字串,則該字串應代表完整的網址模式,包括用於比對來源的模式資訊。如果您提供兩個字串,系統會將第二個字串用作 baseURL,而第一個字串會視為相對於該基底。

無論您提供一個字串或兩個字串,URLPattern 建構函式都會剖析完整的網址模式,將其分割成網址元件,並將較大模式的每個部分對應至相應的元件。也就是說,在幕後,每個使用字串建立的 URLPattern 最終都會以與使用物件建立的等效 URLPattern 相同方式呈現。字串建構函式只是捷徑,適合偏好較精簡介面的使用者。

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

使用字串建立 URLPattern 時,請留意以下幾點。

使用物件建構 URLPattern 時,如果省略屬性,就等同於為該屬性提供 * 通配字。剖析完整網址字串模式時,如果其中一個網址元件缺少值,系統會將該元件視為屬性設為 '',只有在該元件為空白時才會比對。

使用字串時,如果您希望萬用字元用於建構的 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',
});

您也應注意,將字串模式剖析成其元件可能會造成模糊不清的情況。有些字元 (例如 :) 會出現在網址中,但在模式比對語法中也有特殊意義。為避免這種模糊情況,URLPattern 建構函式會假設任何特殊字元都是模式的一部分,而不是網址的一部分。如果您希望系統將含糊字元解讀為網址的一部分,請務必在以字串提供時,使用 \` character. For example, the literal URLabout:blankshould be escaped as`about\:blank'` 逸出該字元。

使用模式

建構 URLPattern 後,您可以選擇兩種使用方式。test()exec() 方法都採用相同的輸入內容,並使用相同的演算法檢查是否相符,唯一的差異在於回傳值。如果與指定輸入內容相符,test() 會傳回 true,否則會傳回 falseexec() 會傳回相符項目的詳細資訊,以及擷取群組,如果沒有相符項目,則會傳回 null。以下範例示範如何使用 exec(),但如果您只需要簡單的布林值傳回值,可以將 test() 換成任何一個 test()

使用 test()exec() 方法的其中一種方法是傳入字串。與建構函式支援的情況類似,如果提供單一字串,該字串應為完整網址,包括來源。如果提供兩個字串,系統會將第二個字串視為 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.

或者,您也可以傳遞建構函式支援的相同類型物件,其中屬性只設為您想比對的網址部分。

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() 時,傳回值會提供輸入網址中對應值的相關資訊。這樣一來,您就不必自行剖析這些值。

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'

匿名和命名群組

將網址字串傳遞至 exec() 後,您會收到一個值,指出哪些部分符合所有模式群組。

傳回值具有與 URLPattern 元件 (例如 pathname) 相對應的屬性。因此,如果群組已定義為 URLPatternpathname 部分,則可在傳回值的 pathname.groups 中找到相符項目。系統會根據對應的模式是匿名或命名群組,以不同方式表示相符項目。

您可以使用陣列索引存取匿名模式比對的值。如果有多個匿名模式,索引 0 會代表最左邊模式的配對值,而 1 和其他索引則會用於後續模式。

在模式中使用命名群組時,系統會將相符項目公開為屬性,其名稱會對應至各個群組名稱。

Unicode 支援和正規化

URLPattern 支援多種 Unicode 字元。

  • 命名群組 (例如 :café) 可包含 Unicode 字元。適用於有效 JavaScript 識別碼 的規則也適用於命名群組。

  • 模式中的文字會根據用於該特定元件網址編碼的相同規則自動編碼。pathname 中的 Unicode 字元會使用百分比編碼,因此 pathname 模式 (例如 /café) 會自動標準化為 /caf%C3%A9hostname 中的萬國碼字元會自動使用 Punycode 編碼,而非百分比編碼。

  • 規則運算式群組只能包含 ASCII 字元。規則運算式語法會導致自動對這些群組中的 Unicode 字元進行編碼的過程變得困難且不安全。如果您想在規則運算式群組中比對 Unicode 字元,就必須手動編碼,例如 (caf%C3%A9)café 相符。

除了編碼 Unicode 字元之外,URLPattern 也會執行網址規格化。舉例來說,pathname 元件中的 /foo/./bar 會折疊為等同的 /foo/bar

如果不確定特定輸入模式的規則是否已完成規格化,請使用瀏覽器的 DevTools 檢查已建構的 URLPattern 例項。

全面整合使用

下方嵌入的 Glitch 示範說明服務工作程的 fetch event handlerURLPattern 的核心用途,將特定模式對應至可產生網路要求回應的非同步函式。這個範例中的概念也適用於其他路由情境,無論是伺服器端還是用戶端。

意見回饋和未來計畫

雖然 URLPattern 的基本功能已在 Chrome 和 Edge 上推出,但我們也計畫新增其他功能。URLPattern 的部分功能仍在開發中,且有許多未解決的問題,關於特定行為仍有待改進。歡迎您試用 URLPattern,並透過 GitHub 問題提供任何意見回饋。

支援範本

path-to-regexp 程式庫提供 compile() function,可有效反轉路由行為。compile() 會取用符記預留位置的模式和值,並傳回網址路徑的字串,其中已替換這些值。

我們希望日後將這項功能加入 URLPattern,但這不在初始版本的範圍內。

啟用未來的網頁平台功能

假設 URLPattern 成為網路平台的固定部分,其他可從路由或模式比對中受益的功能,就能以此做為基本元素進行建構。

我們目前正討論如何使用 URLPattern 實作建議功能,例如服務工作者範圍模式比對使用 PWA 做為檔案處理常式,以及推測預先擷取

特別銘謝

如需完整的致謝清單,請參閱原始說明文件