出处:掘金
原作者:ErpanOmer
我在 Code Review 里最怕看到的代码之一,就是手写正则去解析 URL 参数
每次看到类似下面这样的代码,我的血压就忍不住要升高:
// 一个试图从 URL 里获取'id'参数的函数
function getIdFromUrl(url) {
const match = url.match(/[?&]id=([^&]*)/);
return match ? match[1] : null;
}或者这种 split 链式调用:
const url = 'https://example.com?a=1&b=2';
const paramsStr = url.split('?')[1];
// ...然后再对 paramsStr 按'&'和'='去分割...这段代码,乍一看好像能用。但作为工程师,我们得考虑边界情况:
- 它能处理 URL 编码的字符吗?比如
name=%E5%BC%A0%E4%B8%89 - 它能处理同一个参数出现多次的情况吗?比如
tags=js&tags=css - 如果参数在 hash 后面,它能正确处理吗?
答案是,都不能。手写正则和 split 来处理 URL,是一种极其脆弱和危险的写法
其实,浏览器早就给我们提供了一套官方的、功能强大且极其健壮的工具来处理 URL,那就是 URL 和 URLSearchParams 这两个 Web API。今天,我就想彻底聊聊它们
URL 对象:URL 的结构化表示
别再把 URL 当成一个简单的字符串了。我们可以用 new URL() 构造函数,把它变成一个清晰、易于操作的结构化对象
const urlString = 'https://juejin.cn/user/12345/posts?type=hot&page=2#comments';
// 传入一个URL字符串,或者一个相对路径 + base URL
const myUrl = new URL(urlString);
console.log(myUrl);
一旦你把它变成了 URL 对象,获取它的各个部分就变得极其简单:
console.log(myUrl.protocol); // "https:"
console.log(myUrl.hostname); // "juejin.cn"
console.log(myUrl.port); // "" (因为URL中没有指定)
console.log(myUrl.pathname); // "/user/12345/posts"
console.log(myUrl.search); // "?type=hot&page=2"
console.log(myUrl.hash); // "#comments"它不仅能读,还能写。修改 URL 的任何一部分,都像修改一个普通的 JS 对象属性一样简单:
myUrl.pathname = '/user/98765/articles';
myUrl.hash = '#profile';
myUrl.port = '8080';
// .href 属性会返回拼接好的完整 URL 字符串
console.log(myUrl.href);
// "https://juejin.cn:8080/user/98765/articles?type=hot&page=2#profile"看到没?所有部分都被清晰地解析出来了,你可以像操作普通 JS 对象一样去读写,完全不用担心拼接字符串时漏掉 / 或者 ?
URLSearchParams:查询参数的进价用法
URL 对象本身已经很强大了,而它里面还藏着一个宝藏属性:myUrl.searchParams
这个 searchParams 就是一个 URLSearchParams 的实例,它专门用来处理 ?key=value&key2=value2 这部分。我们也可以单独创建它
const paramsString = 'type=hot&category=frontend&tags=react&tags=vue';
const searchParams = new URLSearchParams(paramsString);
有了它,所有关于查询参数的操作,都有了清晰、标准的方法:
1 . 读取参数 .get()
console.log(searchParams.get('type')); // "hot" console.log(searchParams.get('nonexistent')); // null2. 检查参数是否存在 .has()
console.log(searchParams.has('category')); // true3. 处理同名参数 .getAll()
这是手写正则最头疼的地方。URLSearchParams 能完美处理
console.log(searchParams.getAll('tags')); // ["react", "vue"]4. 修改与添加参数 .set() & .append()
// .set() 会覆盖已有的值
searchParams.set('type', 'new');
// .append() 会在已有的值后面追加,而不会覆盖
searchParams.append('tags', 'css');5. 删除参数 .delete()
searchParams.delete('category');6. 迭代参数 .forEach() 或 for...of
for (const [key, value] of searchParams) {
console.log(`${key}: ${value}`);
}7. 转换回字符串 .toString()
这是最重要的一步。它会自动帮你处理好 URL 编码
// 假设我们添加一个带中文的参数
searchParams.set('author', '张三');
console.log(searchParams.toString());
// "type=new&tags=react&tags=vue&tags=css&author=%E5%BC%A0%E4%B8%89"看到 %E5%BC%A0%E4%B8%89 了吗?它自动帮你把“张三”给 URL 编码了。这一切,如果你用正则去实现,代码量至少多五倍,而且还全是 bug