URLPattern 提供路徑至網路平台

發布日期:2021 年 7 月 22 日

路徑是每個網頁應用程式的關鍵環節。從本質上來說,路由會採用網址,並套用模式比對或其他應用程式專屬邏輯,然後通常會根據結果顯示網頁內容。您可以透過多種方式實作路由:

  • 將路徑對應至磁碟上檔案的伺服器程式碼
  • 單頁應用程式中的邏輯,會等待目前位置的變更,然後建立並顯示對應的 DOM 片段。

雖然沒有明確的標準,但網頁開發人員已傾向採用通用語法來表示網址路徑模式,這與 regular expressions 有許多共通之處,但會加入一些網域專屬的項目,例如用於比對路徑區隔的符記。ExpressRuby on Rails 等熱門伺服器端架構會使用這種語法 (或非常接近的語法),而 JavaScript 開發人員可以使用 path-to-regexpregexpparam 等模組,將該邏輯新增至自己的程式碼。

URLPattern 是網頁平台的新增功能,以這些架構建立的基礎為基礎。其目標是統一路由模式語法,包括支援萬用字元、具名權杖群組、規則運算式群組和群組修飾符。以這個語法建立的 URLPattern 執行個體可以執行常見的路由工作,例如比對完整網址或網址 pathname,以及傳回有關權杖和群組比對的資訊。

直接在網頁平台中提供網址比對功能的另一個好處是,這樣一來,其他也需要比對網址的 API 就能共用常見的語法。

瀏覽器支援和 Polyfill

在 Chrome 和 Edge 95 以上版本中,URLPattern 預設為啟用。

urlpattern-polyfill 程式庫可在瀏覽器或 Node 等環境中使用 URLPattern 介面,這些環境缺乏內建支援。如果您使用 Polyfill,請務必使用功能偵測,確保只有在目前環境缺少支援時才載入。否則,您將失去 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,偶爾也會處理網址的 searchhash 部分。由於這些網址只用於獨立網頁應用程式內的同源路徑,因此一律不必檢查網址的來源部分。

考量來源後,就能開啟更多用途,例如在服務工作人員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(),但如果只想取得布林傳回值,可以將任何範例中的 exec() 換成 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 中的 Unicode 字元會自動使用 Punycode 編碼,而非百分比編碼。

  • 規則運算式群組只能包含 ASCII 字元。規則運算式語法會導致系統難以自動編碼這些群組中的 Unicode 字元,且不安全。如要在規則運算式群組中比對 Unicode 字元,您需要手動進行百分比編碼,例如 (caf%C3%A9) 比對 café

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

如果對特定輸入模式的正規化方式有疑問,請使用瀏覽器的 DevTools 檢查建構的 URLPattern 執行個體。

馬上開始全面整合吧!

Glitch 示範說明 URLPattern 的核心用途,也就是在服務工作站的 fetch event handler 中,將特定模式對應至非同步函式,產生網路要求的回應。這個範例中的概念也適用於其他伺服器端或用戶端路由情境。

意見回饋和未來計畫

雖然 Chrome 和 Edge 已支援 URLPattern 的基本功能, 但我們還計畫新增其他功能。URLPattern 的部分功能仍在開發中,且特定行為仍有許多待解決的問題,歡迎試用 URLPattern,並透過 GitHub 問題提供意見回饋。

支援範本

path-to-regexp 程式庫提供 compile() function,可有效反轉轉送行為。compile() 會採用模式和權杖預留位置的值,並傳回含有這些值的網址路徑字串。

我們希望日後能將這項功能 新增至 URLPattern,但這不屬於初始版本的範圍。

啟用未來的網頁平台功能

假設 URLPattern 成為網路平台既有的部分,其他可從路徑或模式比對獲益的功能,就能以它為基礎建構原始型別。

目前正在討論是否要將 URLPattern 用於建議功能,例如服務工作站範圍模式比對將 PWA 設為檔案處理常式推測預先擷取

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