在 JavaScript 中使用 URL

**由乔·阿塔迪撰写✏️**

URL 是任何 Web 应用的关键部分。如果您的应用向 API 发出请求,那么为这些请求制作正确的 URL 非常重要。所有现代浏览器都支持 `URL` API,它提供了一种解析和操作 URL 的方法。它可以轻松访问 URL 的各个部分。

了解 URL 的各部分

请考虑以下 URL:

`https://example.com/api/search?query=foo&sort=asc#results`

此 URL 由以下部分组成:

  • 协议:https
  • 主机:example.com
  • 路径名:/api/search
  • 查询字符串: ?query=foo&sort=asc
  • 哈希:#results
  • 使用现代 JavaScript,我们可以解析 URL 并根据需要提取这些不同的部分。

    解析 URL

    在较旧的浏览器中,在 `URL` API 可用之前,开发人员解析 URL 的一种方法是使用 ` ` 元素。此元素提供一些基本的 URL 解析。例如,以下是从 URL 中提取查询字符串的一种方法:

    function getQueryString(url) {
      const link = document.createElement('a');
      link.href = url;
      return url.search;
    }

    然而,这种方法有一些缺点:

  • 它需要 DOM 环境,这意味着它无法在 Node.js 等环境中工作
  • 它也没有错误处理——如果将无效的 URL 传递给 href 属性,则不会抛出任何错误
  • 您也可以使用正则表达式来解析 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` 的查询字符串:

  • 搜索属性,它是包含完整查询字符串(包括 ? 字符)的字符串
  • searchParams 属性,它是一个 URLSearchParams 对象
  • 如果您对查询字符串中特定参数的值感兴趣,则可以使用它的 `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` 的优点包括:

  • 您不必担心参数之间的 & 字符
  • 您不需要对参数值进行 URI 编码
  • 您不需要使用字符串连接
  • 迭代查询参数

    如果没有“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 进行切分以满足您的应用需求!**进一步阅读:**

  • URL 接口(MDN)
  • URLPattern API(MDN)
  • 几分钟内即可设置 LogRocket 的现代错误跟踪:

  • 访问 https://logrocket.com/signup/ 获取应用程序 ID。
  • 通过 NPM 或脚本标签安装 LogRocket。LogRocket.init() 必须在客户端调用,而不是服务器端。
  • 新版本:

    $ npm i --save logrocket 
    
    // Code:
    
    import LogRocket from 'logrocket'; 
    LogRocket.init('app/id');

    脚本标记:

    Add to your HTML:
    
    
    

    3.(可选)安装插件以便与您的堆栈进行更深入的集成:

  • Redux 中间件
  • ngrx 中间件
  • Vuex 插件
  • 立即开始