在 JavaScript 中使用 URL
**由乔·阿塔迪撰写✏️**
URL 是任何 Web 应用的关键部分。如果您的应用向 API 发出请求,那么为这些请求制作正确的 URL 非常重要。所有现代浏览器都支持 `URL` API,它提供了一种解析和操作 URL 的方法。它可以轻松访问 URL 的各个部分。
了解 URL 的各部分
请考虑以下 URL:
`https://example.com/api/search?query=foo&sort=asc#results`
此 URL 由以下部分组成:
使用现代 JavaScript,我们可以解析 URL 并根据需要提取这些不同的部分。
解析 URL
在较旧的浏览器中,在 `URL` API 可用之前,开发人员解析 URL 的一种方法是使用 ` ` 元素。此元素提供一些基本的 URL 解析。例如,以下是从 URL 中提取查询字符串的一种方法:
function getQueryString(url) { const link = document.createElement('a'); link.href = url; return url.search; }
然而,这种方法有一些缺点:
您也可以使用正则表达式来解析 URL 的各个部分,但这很繁琐并且容易出错。
使用 `URL` API 解析 URL 非常简单。只需将要解析的 URL 传递给 `URL` 构造函数即可。如果 URL 字符串有效,您将获得一个 `URL` 对象,其中包含 URL 各个部分的属性:
const url = new URL('https://example.com/api/search?query=foobar'); console.log(url.host); // example.com console.log(url.pathname); // /api/search console.log(url.search); // ?query=foobar
解析查询字符串
您可以通过两种方式访问 `URL` 的查询字符串:
如果您对查询字符串中特定参数的值感兴趣,则可以使用它的 `get` 方法通过其名称获取参数:
const url = new URL('https://example.com/api/search?query=foobar&maxResults=10'); console.log(url.searchParams.get('query'); // foobar console.log(url.searchParams.get('maxResults'); // 10
如果有多个同名的参数,可以使用“getAll”来获取包含该名称的所有值的数组:
const url = new URL('https://example.com/api/search?tag=tag1&tag=tag2&tag=tag3'); console.log(url.searchParams.getAll('tag')); // ['tag1', 'tag2', 'tag3']
构建查询字符串
手动构建查询字符串可能比较棘手,尤其是当任何查询参数包含需要转义的特殊字符时。例如,如果查询参数需要包含“&”字符,则需要将其编码为“%26”。为了解决这些情况,您需要使用“encodeURIComponent”函数:
let queryString = 'foo=bar'; queryString += '&baz=qux'; queryString += '&tag=' + encodeURIComponent('one&two'); console.log(queryString); // foo=bar&baz=qux&tag=one%26two
您可以使用 `URLSearchParams` 对象更安全地构建查询字符串:
const params = new URLSearchParams(); params.append('foo', 'bar'); params.append('baz', 'qux'); params.append('tag', 'one&two'); console.log(params.toString()); // foo=bar&baz=qux&tag=one%26two
使用 `URLSearchParams` 的优点包括:
迭代查询参数
如果没有“URLSearchParams”对象,迭代查询字符串中的参数会有些困难。您需要多次拆分字符串 — 首先拆分成键/值对组,然后再次拆分键和值:
function listQueryParams(queryString) { queryString.split('&').forEach(param => { const [key, value] = param.split('='); console.log(`${key}: ${value}`); }); }
如果参数可能包含编码字符,则还需要对其进行解码:
function listQueryParams(queryString) { queryString.split('&').forEach(param => { const [key, value] = param.split('='); console.log(`${key}: ${decodeURIComponent(value)}`); }); }
相反,您可以使用 `URLSearchParams` 的 `entries` 方法来迭代键/值对:
function listQueryParams(queryString) { const params = new URLSearchParams(queryString); params.entries().forEach(([key, value]) => console.log(`${key}: ${value}`)); }
构建完整的 URL
以下是使用基本 URL 和一些查询参数构建 URL 的完整示例:
const url = new URL('https://example.com/api/search'); url.searchParams.append('query', 'test'); url.searchParams.append('tag', 'tag1'); url.searchParams.append('tag', 'tag2'); // https://example.com/api/search?query=test&tag=tag1&tag=tag2 console.log(url.toString());
检查有效的 URL
您可能尝试使用正则表达式来验证 URL,但众所周知,编写一个能够完全捕获有效 URL 字符串的正则表达式非常困难。
相反,您可以使用 `URL` API。如果您提供无效的 URL,`URL` 构造函数将抛出错误。您可以使用它来检查 URL 是否有效:
function isValidURL(url) { try { new URL(url); return true; } catch (error) { return false; } }
使用较新的浏览器,这甚至更加容易。有一个较新的 `URL.canParse` 静态方法,只需一行代码即可执行类似的验证。与上面的 `isValidURL` 函数一样,它接受一个潜在的 URL 字符串,并根据 URL 字符串的有效性返回 `true` 或 `false`。
创建相对 URL
`URL` API 具有强大的机制来解析相对 URL。通常,如果 `URL` 构造函数的参数不是完整、有效的 URL,则会抛出错误。但是,您可以指定第二个参数作为构建相对 URL 的基础。如果您使用双参数方法,则第一个参数不必是有效的 URL,但第二个参数必须是有效的 URL。
我们先来看一个简单的案例:
new URL('/about', 'https://example.com').href;
`URL` 构造函数采用 `https://example.com` 的基本 URL,并添加相对路径 `/about`,从而得到 `https://example.com/about`。
那这个呢:
new URL('profile', 'https://example.com/users').href;
您可能认为这是“https://example.com/users/profile”,但实际上却是“https://example.com/profile”。这就像一个相对链接;它采用父路径段(即“example.com”的根),然后添加“profile”。
我们再看一个使用相对 URL 的例子。你也可以使用 `..` 返回路径层次结构:
new URL('../profile', 'https://example.com/users/123').href;
这个 URL 的地址是 `https://example.com/profile`。请记住,相对 URL 从父路径段开始。然后,这个 URL 中包含 `..`,它又向上延伸了一个路径段。
如果您使用相对 URL 调用 `URL` 构造函数并为基本 URL 指定无效或不完整的 URL,则会出现错误。如果您使用相对 URL 而没有完整的基本 URL,也会出现错误:
new URL('../profile', '/about'); // error! new URL('../profile'); // error
使用 window.location 对象
您可能熟悉 `window.location` 对象,它表示当前页面的 URL。此对象还具有 `href` 和 `pathname` 等属性,因此您可能认为它是一个 `URL` 对象。这是一个不同的对象,即 `Location`,它具有与 `URL` 相同的一些属性,但也缺少一些属性(例如 `searchParams` 属性)。
即使它不是 `URL` 对象,您仍然可以使用 `window.location` 来构造新的 `URL` 对象。您可以将 `window.location` 传递给 `URL` 构造函数,以基于当前 URL 创建一个包含 `searchParams` 等内容的全新完整 `URL`,或者您甚至可以在构造相对 URL 时将其用作基本 URL:
new URL('/profile', window.location).href;
使用 URLPattern 匹配 URL 中的模式
使用“URL”可以轻松从 URL 获取路径。例如,在 URL“https://example.com/api/users/123/profile”中,路径名称为“/api/users/123/profile”。如果我们只想从此 URL 获取用户 ID“123”,该怎么办?
正如我们前面讨论的,创建适当的正则表达式来验证和提取 URL 的各个部分可能很困难。
目前,此功能还未在所有浏览器中可用,但您可以使用 `URLPattern` API 匹配和提取 URL 的部分内容,以匹配您指定的模式。这对于单页应用程序 (SPA) 中的客户端路由等功能特别有用。
以用户个人资料 URL 为例,让我们创建一个 `URLPattern` 来获取用户 ID。我们可以使用前导 `:` 字符来表示命名占位符,稍后可以使用它来匹配 URL 的该部分:
const pattern = new URLPattern('https://example.com/api/users/:userId/profile'); const matcher = pattern.exec('https://example.com/api/users/123/profile'); console.log(matcher.pathname.groups.userId); // 123
当您对“URLPattern”调用“exec”时,它需要一个有效的 URL。它会返回一个匹配器对象,其中包含 URL 各部分(“协议”、“主机”、“路径名”等)的属性。每个属性还具有一个“groups”属性,它将占位符名称(如“:userId”)映射到 URL 中的值。
如果您只关心匹配 URL 的一部分(例如我们在此处所做的路径名),您也可以在 URL 模式中指定通配符。或者,您可以传递一个包含您想要匹配的 URL 部分的对象,而不是 URL 字符串:
new URLPattern('https://*/api/users/:userId/profile'); new URLPattern({ pathname: '/api/users/:userId/profile' });
`URLPattern` API 仍未在所有浏览器中可用。在撰写本文时,它尚未在 Firefox 或 Safari 中得到支持。您可以在 CanIUse.com 上查看最新的浏览器支持信息。
概括
`URL` API 是一个通用接口,用于在 JavaScript 中构建、验证和操作 URL。与手动解析或正则表达式相比,它更安全,出错率更低。通过使用 `URLSearchParams` 对象,您可以构建查询字符串,而不必担心字符串连接或编码特殊字符。
`URLPattern` API 更进一步,支持通配符和命名占位符,因此您可以对 URL 进行切分以满足您的应用需求!**进一步阅读:**
几分钟内即可设置 LogRocket 的现代错误跟踪:
新版本:
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
脚本标记:
Add to your HTML:
3.(可选)安装插件以便与您的堆栈进行更深入的集成:
立即开始