2021 年 12 月 12 日

URL 物件

內建的 URL 類別提供一個便利的介面來建立和剖析 URL。

沒有任何網路方法需要一個 URL 物件,字串就足夠了。因此,技術上我們不必使用 URL。但有時它真的很有幫助。

建立 URL

建立新的 URL 物件的語法

new URL(url, [base])
  • url – 完整的 URL 或僅路徑(如果設定了基礎,請參閱下方),
  • base – 一個可選的基礎 URL:如果設定了,而 url 參數僅有路徑,則 URL 會相對於 base 產生。

例如

let url = new URL('https://javascriptinfo.dev.org.tw/profile/admin');

這兩個 URL 是相同的

let url1 = new URL('https://javascriptinfo.dev.org.tw/profile/admin');
let url2 = new URL('/profile/admin', 'https://javascriptinfo.dev.org.tw');

alert(url1); // https://javascriptinfo.dev.org.tw/profile/admin
alert(url2); // https://javascriptinfo.dev.org.tw/profile/admin

我們可以輕鬆地根據現有 URL 的路徑建立新的 URL

let url = new URL('https://javascriptinfo.dev.org.tw/profile/admin');
let newUrl = new URL('tester', url);

alert(newUrl); // https://javascriptinfo.dev.org.tw/profile/tester

URL 物件讓我們可以立即存取其組成部分,因此這是一個解析 URL 的好方法,例如

let url = new URL('https://javascriptinfo.dev.org.tw/url');

alert(url.protocol); // https:
alert(url.host);     // javascript.info
alert(url.pathname); // /url

以下是 URL 組成部分的秘笈

  • href 是完整的 URL,與 url.toString() 相同
  • protocol 以冒號字元 : 結尾
  • search – 參數字串,以問號 ? 開頭
  • hash 以井字號字元 # 開頭
  • 如果存在 HTTP 驗證,也可能有 userpassword 屬性:http://login:[email protected](未在上方顯示,很少使用)。
我們可以將 URL 物件傳遞給網路(以及大多數其他)方法,而不是字串

我們可以在 fetchXMLHttpRequest 中使用 URL 物件,幾乎可以在所有預期 URL 字串的地方使用。

一般來說,URL 物件可以傳遞給任何方法,而不是字串,因為大多數方法會執行字串轉換,將 URL 物件轉換為包含完整 URL 的字串。

SearchParams “?…”

假設我們要建立一個具有給定搜尋參數的 URL,例如 https://google.com/search?query=JavaScript

我們可以在 URL 字串中提供它們

new URL('https://google.com/search?query=JavaScript')

…但如果參數包含空格、非拉丁字母等,則需要對其編碼(下方會詳細說明)。

因此,有一個 URL 屬性:url.searchParams,一個類型為 URLSearchParams 的物件。

它提供方便的搜尋參數方法

  • append(name, value) – 透過 name 新增參數,
  • delete(name) – 透過 name 移除參數,
  • get(name) – 透過 name 取得參數,
  • getAll(name) – 取得所有具有相同 name 的參數(這是可能的,例如 ?user=John&user=Pete),
  • has(name) – 檢查是否存在透過 name 的參數,
  • set(name, value) – 設定/取代參數,
  • sort() – 根據名稱排序參數,很少需要,
  • …它也是可迭代的,類似於 Map

包含空格和標點符號的參數範例

let url = new URL('https://google.com/search');

url.searchParams.set('q', 'test me!'); // added parameter with a space and !

alert(url); // https://google.com/search?q=test+me%21

url.searchParams.set('tbs', 'qdr:y'); // added parameter with a colon :

// parameters are automatically encoded
alert(url); // https://google.com/search?q=test+me%21&tbs=qdr%3Ay

// iterate over search parameters (decoded)
for(let [name, value] of url.searchParams) {
  alert(`${name}=${value}`); // q=test me!, then tbs=qdr:y
}

編碼

有一個標準 RFC3986 定義哪些字元可以在 URL 中使用,哪些字元不能使用。

那些不能使用的字元必須編碼,例如非拉丁字母和空格,用它們的 UTF-8 編碼取代,並加上 % 前綴,例如 %20(出於歷史原因,空格可以用 + 編碼,但這是一個例外)。

好消息是 URL 物件會自動處理所有這些。我們只需要提供所有未編碼的參數,然後將 URL 轉換為字串。

// using some cyrillic characters for this example

let url = new URL('https://ru.wikipedia.org/wiki/Тест');

url.searchParams.set('key', 'ъ');
alert(url); //https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%81%D1%82?key=%D1%8A

如你所見,URL 路徑中的 Тест 和參數中的 ъ 都已編碼。

URL 變長了,因為每個西里爾字母在 UTF-8 中都用兩個位元組表示,所以有兩個 %.. 實體。

編碼字串

在舊時代,在 URL 物件出現之前,人們使用字串作為 URL。

到目前為止,URL 物件通常更方便,但字串仍然可以使用。在許多情況下,使用字串會讓程式碼更短。

不過,如果我們使用字串,則需要手動編碼/解碼特殊字元。

有內建函式可以做到這一點

一個自然的問題是:「encodeURIComponentencodeURI 有什麼區別?我們什麼時候應該使用哪一個?」

如果我們看看上圖中拆分成組成部分的 URL,這很容易理解。

https://site.com:8080/path/page?p1=v1&p2=v2#hash

如我們所見,:?=&# 等字元在 URL 中是允許的。

…另一方面,如果我們看單一的 URL 組成部分,例如搜尋參數,這些字元必須編碼,以免破壞格式。

  • encodeURI 只編碼在 URL 中完全禁止的字元。
  • encodeURIComponent 編碼相同的字元,此外,還編碼字元 #$&+,/:;=?@

因此,對於整個 URL,我們可以使用 encodeURI

// using cyrillic characters in url path
let url = encodeURI('http://site.com/привет');

alert(url); // http://site.com/%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82

…而對於 URL 參數,我們應該改用 encodeURIComponent

let music = encodeURIComponent('Rock&Roll');

let url = `https://google.com/search?q=${music}`;
alert(url); // https://google.com/search?q=Rock%26Roll

將其與 encodeURI 進行比較

let music = encodeURI('Rock&Roll');

let url = `https://google.com/search?q=${music}`;
alert(url); // https://google.com/search?q=Rock&Roll

正如我們所見,encodeURI 沒有編碼 &,因為這是 URL 整體中合法的字元。

但是,我們應該對搜尋參數內的 & 進行編碼,否則,我們會得到 q=Rock&Roll – 這實際上是 q=Rock 加上某些模糊參數 Roll。並非預期結果。

因此,我們應該僅對每個搜尋參數使用 encodeURIComponent,才能正確將其插入 URL 字串。最安全的方法是編碼名稱和值,除非我們絕對確定它只包含允許的字元。

URL 相比的編碼差異

類別 URLURLSearchParams 基於最新的 URI 規格:RFC3986,而 encode* 函數基於已過時的版本 RFC2396

有一些差異,例如 IPv6 位址的編碼方式不同

// valid url with IPv6 address
let url = 'http://[2607:f8b0:4005:802::1007]/';

alert(encodeURI(url)); // http://%5B2607:f8b0:4005:802::1007%5D/
alert(new URL(url)); // http://[2607:f8b0:4005:802::1007]/

正如我們所見,encodeURI 取代了方括號 [...],這是錯誤的,原因是:在 RFC2396(1998 年 8 月)制定時,IPv6 URL 尚未存在。

這種情況很少見,encode* 函數在大部分時間都能正常運作。

教學課程地圖

留言

留言前請先閱讀…
  • 如果您有改進建議,請 提交 GitHub 議題 或提出拉取請求,而不是留言。
  • 如果您無法理解文章中的某些內容,請詳細說明。
  • 若要插入一些程式碼,請使用 <code> 標籤,若要插入多行,請將其包覆在 <pre> 標籤中,若要插入超過 10 行,請使用沙盒(plnkrjsbincodepen…)