内置的 URL 类提供了一个方便的接口来创建和解析 URL。
没有网络方法需要确切的 URL 对象,字符串就足够了。所以从技术上讲,我们不必使用 URL。但有时它确实很有帮助。
创建 URL
创建新的 URL 对象的语法
new URL(url, [base])
url– 完整的 URL 或仅路径(如果设置了 base,请参见下文),base– 可选的基础 URL:如果设置了该参数并且url参数只有路径,则 URL 是相对于base生成的。
例如
let url = new URL('https://javascript.js.cn/profile/admin');
这两个 URL 相同
let url1 = new URL('https://javascript.js.cn/profile/admin');
let url2 = new URL('/profile/admin', 'https://javascript.js.cn');
alert(url1); // https://javascript.js.cn/profile/admin
alert(url2); // https://javascript.js.cn/profile/admin
我们可以轻松地基于相对于现有 URL 的路径创建新的 URL
let url = new URL('https://javascript.js.cn/profile/admin');
let newUrl = new URL('tester', url);
alert(newUrl); // https://javascript.js.cn/profile/tester
URL 对象允许我们立即访问其组件,因此它是解析 URL 的一种好方法,例如:
let url = new URL('https://javascript.js.cn/url');
alert(url.protocol); // https:
alert(url.host); // javascript.info
alert(url.pathname); // /url
以下是 URL 组件的速查表
href是完整的 URL,与url.toString()相同protocol以冒号字符:结尾search– 一串参数,以问号?开头hash以井号#开头- 如果存在 HTTP 身份验证,还可能存在
user和password属性:http://login:password@site.com(上面没有画出,很少使用)。
URL 对象传递给网络(以及大多数其他)方法,而不是字符串我们可以在 fetch 或 XMLHttpRequest 中使用 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 对象通常更方便,但字符串仍然可以使用。在许多情况下,使用字符串可以使代码更短。
但是,如果我们使用字符串,我们需要手动编码/解码特殊字符。
为此,有内置函数
- encodeURI - 将整个 URL 编码。
- decodeURI - 将其解码回来。
- encodeURIComponent - 编码 URL 组件,例如搜索参数、哈希或路径名。
- decodeURIComponent - 将其解码回来。
一个自然的问题是:“encodeURIComponent 和 encodeURI 之间有什么区别?我们什么时候应该使用哪一个?”
如果我们看一下上面的图片中分成组件的 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 相比的编码差异类 URL 和 URLSearchParams 基于最新的 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* 函数在大多数情况下都能正常工作。
评论
<code>标签,对于多行代码,请将其包装在<pre>标签中,对于超过 10 行的代码,请使用沙盒(plnkr,jsbin,codepen…)。