LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

比字符串拼接优雅一百倍的浏览器原生 URL 处理方式

admin
2026年6月30日 15:45 本文热度 64

你有没有写过这样的代码?

const query = window.location.search.slice(1);const pairs = query.split('&');const params = {};pairs.forEach(pair => {  const [key, val] = pair.split('=');  params[key] = decodeURIComponent(val);});

或者这样:

const url = 'https://api.example.com/users?page=' + page + '&limit=' + limit;

这些写法虽然能用,但隐藏着许多坑:特殊字符编码遗漏、重复参数处理不当、相对路径解析错误……更重要的是,它们让代码变得脆弱且难以维护。

好消息是,浏览器早就为我们准备了一套标准 API,专门用来 安全、轻松地驾驭 URL。这套 API 就是 URL 和 URLSearchParams。掌握它们,你不仅能彻底告别手写解析逻辑,还能让代码更清晰、更健壮。接下来,我们就一步步走进这个实用工具箱。

一、认识 URL 的真面目

1.1 创建一个 URL 对象

要使用 URL API,第一步就是实例化一个 URL 对象:

const url = new URL('https://example.com:8080/path/to/page?id=123&name=追风#section');

构造函数接收两个参数:

  • url:完整的 URL 字符串(可以是相对路径)。

  • base(可选):基准 URL,当第一个参数是相对路径时使用。

// 使用 base 解析相对路径const url = new URL('/api/users''https://example.com');console.log(url.href); // "https://example.com/api/users"

注意:base 参数必须是 绝对 URL,否则会抛出 TypeError。

1.2 URL 的“骨骼结构”

一个 URL 就像一栋房子,有门牌号、楼层、房间号等组成部分。URL 对象把这些部分都暴露成了属性,方便你直接读取或修改,一个完整的 URL 可以拆解为以下各部分:

// 示例urlhttps://user:pass@example.com:8080/path/to/page?id=123#section\___/   \_______/ \_________/ \__/\____________/ \______/ \______/  |         |          |        |        |           |        |protocol  username    hostname  port    pathname    search    hash

属性

作用

示例值

href

完整 URL 字符串

见示例 url

protocol

协议(含冒号)

https:

username

用户名

user

password

密码

pass

host

主机+端口

example.com:8080

hostname

主机名

example.com

port

端口号

8080

pathname

路径

/path/to/page

search

查询字符串(含?)

?id=123&name=追风

searchParams

URLSearchParams 对象

见下节

hash

片段(含#)

#section

origin

来源(只读)

https://example.com:8080

示例:

const url = new URL('https://user:pass@example.com:8080/path/to/page?id=123#section');console.log(url.protocol);   // "https:"console.log(url.username);   // "user"console.log(url.host);       // "example.com:8080"console.log(url.hostname);   // "example.com"console.log(url.port);       // "8080"console.log(url.pathname);   // "/path/to/page"console.log(url.search);     // "?id=123"console.log(url.hash);       // "#section"console.log(url.origin);     // "https://example.com:8080"

看到这里,你会发现:再也不用自己写正则去抠 URL 的各个部分了——浏览器都帮你拆好了。

二、查询参数:从“手忙脚乱”到“游刃有余”

查询参数(?key=value&...)是 URL 中最常变动的部分,也是容易出错的重灾区。URLSearchParams 就是为此而生的专属工具。

2.1 获取参数对象

你既可以从已有的 URL 实例中拿,也可以独立创建:

// 从 URL 获取const url = new URL('https://example.com?id=123&name=追风&tags=js&tags=web');const params = url.searchParams;
// 独立创建(支持多种数据源)const p1 new URLSearchParams('id=123&name=追风');const p2 new URLSearchParams({ id'123', name'追风' });const p3 new URLSearchParams([['id''123'], ['name''追风']]);

2.2 增删改查,一气呵成

URLSearchParams 提供了一套完整的方法,让你像操作对象一样操作参数:

方法

作用

示例

get(key)

获取第一个匹配值

params.get('id') → "123"

getAll(key)

获取所有匹配值(数组)

params.getAll('tags') → ["js","web"]

has(key)

是否存在该参数

params.has('name') → true

append(key, value)

追加一个值(允许重复)

params.append('tags','css')

set(key, value)

设置或覆盖该参数(删除所有同名的旧值)

params.set('page','2')

delete(key)

删除该参数的所有值

params.delete('name')

toString()

序列化为查询字符串(不含 ?)

"id=123&name=追风"

sort()

按键名排序(用于缓存或签名)

params.sort()

forEach() / entries() / keys() / values()

遍历

见下文

实战演练

const params = new URLSearchParams();
// 追加params.append('tags''js');params.append('tags''web');params.append('tags''css');
// 设置单一值params.set('name''追风');params.set('page''1');console.log(params.get('tags'));       // "js"(只返回第一个)console.log(params.getAll('tags'));    // ["js", "web", "css"]
// 覆盖params.set('page''2');console.log(params.get('page'));   // "2"
// 删除params.delete('tags');console.log(params.has('tags'));   // false
// 序列化console.log(params.toString());  // 'name=%E8%BF%BD%E9%A3%8E&page=2'// 注意中文被自动编码了,这正是我们需要的
// 遍历(支持 for...of)for (const [key, value] of params) {  console.log(`${key}${value}`);}// 输出:name: 追风 / page: 2
// 排序(按 key 的 Unicode 码点)params.sort();console.log(params.toString());  // "name=...&page=2"(name 在 page 前)

2.3 为什么别再手写解析?

对比一下,你会立刻明白差别:

传统手写方式(不推荐)

function getParam(name) {  const match = location.search.match(new RegExp('[?&]' + name + '=([^&]*)'));  return match ? decodeURIComponent(match[1]) : null;}// 问题:无法处理重复参数、某些特殊字符、编码不一致等边缘情况。

URL API 方式

const params = new URLSearchParams(location.search);const value = params.get('name');    // 自动解码const all = params.getAll('tags');   // 支持重复

简单、可靠、无歧义。这就是标准化的力量

三、相对路径解析:再也不用纠结 ../ 了

在拼接 URL 时,经常遇到相对路径。URL 构造函数借助 base 参数,可以像浏览器一样解析路径关系:

const base = 'https://example.com/docs/api/';console.log(new URL('overview.html'base).href);// "https://example.com/docs/api/overview.html"
console.log(new URL('../index.html'base).href);// "https://example.com/docs/index.html"   (回到上一级)
console.log(new URL('/absolute/path'base).href);// "https://example.com/absolute/path"     (以 / 开头表示根路径)
console.log(new URL('https://other.com'base).href);// "https://other.com"   (绝对 URL 完全忽略 base)

关键细节:base 是否以 / 结尾,会影响拼接行为:

const base1 = 'https://example.com/docs/api';    // 无结尾斜杠const base2 = 'https://example.com/docs/api/';   // 有结尾斜杠console.log(new URL('overview.html', base1).href); // "https://example.com/docs/overview.html" (替换了最后一段 "api")console.log(new URL('overview.html', base2).href);// "https://example.com/docs/api/overview.html" (追加在目录下)

因此,如果你要拼接到目录下,请确保 base 以 / 结尾。

四、动态构建 URL:像搭积木一样灵活

URL 对象的所有属性(除了只读的 origin)都是可写的。你可以随时修改任意部分,然后通过 href 获取新的完整 URL。

const url = new URL('https://example.com/page');// 逐个修改url.protocol = 'http:';url.hostname = 'api.example.com';url.port = '3000';url.pathname = '/v2/users';url.searchParams.set('id''456');url.hash = '#details';console.log(url.href);// "http://api.example.com:3000/v2/users?id=456#details"

这种“修改即生效”的方式,非常适合在函数中根据参数动态生成 URL:

/** * 构建 API 请求 URL * @param {stringendpoint - 接口路径,如 "users" * @param {objectparams - 查询参数对象 * @param {stringbase - 基础域名(可选) */function buildApiUrl(endpoint, params = {}, base = 'https://api.example.com') {  const url = new URL(endpoint, base);  const searchParams = url.searchParams;  Object.entries(params).forEach(([key, value]) => {    // 支持数组类型的参数(多个同名)    if (Array.isArray(value)) {      value.forEach(v => searchParams.append(key, v));    } else {      searchParams.set(key, value);    }  });  return url.toString();}// 调用示例const result = buildApiUrl('users', {  page2,  limit20,  keywords'前端开发',  tags: ['js''vue']});console.log(result);// "https://api.example.com/users?page=2&limit=20&keywords=%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91&tags=js&tags=vue"

这个函数自动处理了编码、数组参数、路径拼接,比用模板字符串拼接安全得多。

五、编码与解码:别再为 %20 头疼了

URL 中有些字符(如空格、&、#)必须转义成百分号编码形式。新手容易忽略,老手也常手滑。而 URL API 的 最大优点之一,就是自动处理编码 。

5.1 自动编码,自动解码

const url = new URL('https://example.com');url.searchParams.set('q''a b&c=d#e');console.log(url.search);   // '?q=a+b%26c%3Dd%23e'// 空格 → +,& → %26,= → %3D,# → %23// 取值时自动解码console.log(url.searchParams.get('q'));  // 'a b&c=d#e'

你 完全不需要手动调用 encodeURIComponent,只要通过 searchParams.set/append 赋值,API 内部会做好一切。

5.2 如果需要手动编码

虽然大部分场景不需要,但了解一下也无妨:

// encodeURIComponent —— 编码查询参数的值(最常用)encodeURIComponent('追风 & 贪狼');  // '%E8%BF%BD%E9%A3%8E%20%26%20%E8%B4%AA%E7%8B%BC'// encodeURI —— 编码整个 URL(保留 : / ? 等分隔符)encodeURI('https://example.com/你好'); // "https://example.com/%E4%BD%A0%E5%A5%BD"// 解码对应decodeURIComponent('%E8%BF%BD%E9%A3%8E');  // "追风"

建议:在 URL 和 URLSearchParams 能覆盖的场景下,尽量别手动编码,既省心又避错。

六、真实场景中的妙用

6.1 获取当前页面的参数

// 假设当前地址为 https://myblog.com?utm_source=google&utm_medium=emailconst params = new URLSearchParams(window.location.search);const source = params.get('utm_source');   // "google"const medium = params.get('utm_medium');   // "email"

6.2 在 fetch 请求中拼接参数

async function fetchProducts(filters) {  const url = new URL('/api/products'window.location.origin);  // 批量设置参数  Object.keys(filters).forEach(key => {    if (filters[key] !== undefined && filters[key] !== null) {      url.searchParams.set(key, filters[key]);    }  });  const response = await fetch(url);  return response.json();}// 调用fetchProducts({ category'books'minPrice20maxPrice100 });

6.3 表单数据一键转查询参数

const form = document.querySelector('#search-form');form.addEventListener('submit'(e) => {  e.preventDefault();  const formData = new FormData(form);  const params = new URLSearchParams(formData);  // 比如 formData 含 keyword: "vue", category: "frontend"  window.location.search = params.toString();   // 跳转到带参数的新地址});

6.4 生成签名 URL(防篡改)

很多接口需要在参数后附加签名,要求参数必须按字母序排列,以保证签名一致性:

function signUrl(baseUrl, params, secretKey) {  const url = new URL(baseUrl);  // 加入公开参数  Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));  // 排序参数  url.searchParams.sort();  // 假设有一个 HMAC 签名函数  const signature = hmacSHA256(secretKey, url.searchParams.toString());  url.searchParams.set('sign', signature);  return url.toString();}

这里的 sort() 方法让参数顺序固定,是签名前的关键一步。

七、两个静态方法:对象 URL 与合法性检测

7.1 URL.createObjectURL() —— 为 Blob/File 创建临时链接

当你需要预览用户上传的图片、下载动态生成的文件时,这个 API 非常有用:

// 用户选择文件后const input = document.querySelector('input[type="file"]');input.addEventListener('change'(e) => {  const file = e.target.files[0];  if (file) {    const objectURL = URL.createObjectURL(file);    document.querySelector('#preview').src = objectURL;    // 重要:使用完后释放内存,避免泄漏    // 通常在图片加载完成后或组件卸载时调用    // URL.revokeObjectURL(objectURL);  }});

createObjectURL 生成的 URL 格式类似 blob:https://example.com/xxxxx-xxxx,它指向浏览器内存中的对象。用完一定要调用 revokeObjectURL 释放资源,尤其是在大量文件预览的场景下。

7.2 URL.canParse() —— 安全检测(较新)

在尝试 new URL() 之前,先判断字符串是否合法,可以避免抛出异常:

function safeParse(input, base) {  if (URL.canParse(input, base)) {    return new URL(input, base);  }  return null;  // 或提示用户}const userInput = 'https://example.com';const url = safeParse(userInput);if (url) {  // 安全使用else {  alert('您输入的网址不合法');}

兼容性提示:canParse 在 2023 年后的浏览器(Chrome 120+、Firefox 115+、Safari 17+)中支持,Node.js 22+ 也支持。如果兼容性要求高,可以自己用 try...catch 替代。

八、避坑指南与最佳实践

推荐做法:

1. 使用 URL 构造,不用字符串拼接

new URL('/api', base) 比 base + '/api' 可靠得多。

2. 通过 searchParams 操作参数,不手写正则

自动编码、处理重复值、排序,这些都是手写难以完美实现的。

3. 记住 origin 是只读的

想改协议或主机?直接改 protocol 和 hostname,不能改 origin。

4. 对象 URL 及时释放

每次 createObjectURL 都要配对 revokeObjectURL,尤其在 SPA 中注意组件卸载时清理。

5. 善用 sort() 确保参数顺序

对缓存键生成或签名计算很有帮助。

常见错误

// 错误1:base 不是绝对 URLnew URL('path''relative');  // TypeError// 正确:base 必须带协议new URL('path''https://example.com');
// 错误2:直接拼接查询参数,忘记编码const url = 'https://example.com?q=' + 'a b&c';// 正确const url = new URL('https://example.com');url.searchParams.set('q''a b&c');
// 错误3:混淆 host 和 hostname// host 包含端口,hostname 不含端口
// 错误4:直接给 search 赋值,覆盖已有参数url.search = '?a=1';  // 其他参数全部丢失// 正确:用 searchParams 进行增量修改url.searchParams.set('a''1');

兼容性一览

  • URL 和 URLSearchParams:所有现代浏览器(Chrome、Firefox、Safari、Edge)均支持,IE 不支持

  • Node.js:从 v10 开始全局可用,无需额外引入。

  • URL.canParse():较新,不支持时建议用 try...catch 回退。

总结

核心工具

主要职责

new URL()

解析、修改、拼接 URL 的各个部分

url.searchParams

对查询参数进行增删改查、遍历、排序

URL.createObjectURL()

为前端生成的 Blob/File 创建可访问的临时链接

URL.canParse()

安全地预检 URL 字符串是否合法

一句话记住:凡是需要和 URL 打交道的地方,就优先想到 URL 和 URLSearchParams——它们能让你写出更干净、更安全、更不易出错的代码。


该文章在 2026/7/1 14:41:51 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-9  粤公网安备44030602007207号