前序
在我们日常开发过程中,正则表达式是一个经常用到的知识点,它可以帮你更好的处理字符串,根据各种规则做字符串的替换,校验,匹配,查询等等一系列操作。而本文就会介绍到如何利用模板正则表达式去动态地替换目标字符。
案例
现在有这样一个需求,在一段URL上设置有参数名以及参数值地占位符,这个占位符需要真正的参数值去替换。这是很常见的一个需求,比如用token,userId等拼接在URL上。一般为了获取URL上面的参数我们会用到一些工具包(如:query-string)或者直接new一个URL对象(new URL('url')).但是这些都无法满足替换参数占位符的需求。
解决方案一
接下来我们手动实现下这个需求:
const testURL = "http://www.google.com/?token={token}";
const replaceURLParams = (url, paramsValue) => {
if(!url) return url;
const reg = new RegExp(/(token=\{token\})/);
return url.replace(reg, `token=${paramsValue}`);
}
replaceURLParams(testURL, "abc1234"); // http://www.google.com/?token=abc1234
看起来并没什么问题,完美实现需求。那么问题来了,如果一个URL里面存在多个参数如何处理呢?或者后期又有新的参数加进来了又怎么处理?参数名字变了又如何处理?显然,目前这个解决方案并不能很好地满足这样的需求以及后续的变更。
解决方案二
接下来我们可以根据参数名去动态定义正则表达式的规则,然后再通过捕获组去替换占位符
const replaceURLParams = (url, paramsName, paramsValue) => {
if(!url) return url;
const reg = new RegExp("\\b("+paramsName+"=).*?(#|&|$)");
if(url.search(reg) >= 0) {
//`$n` 代表 插入第 `n`(索引从 1 开始)个捕获组,其中 `n` 是小于 100 的正整数。
return url.replace(reg, "$1" + paramsValue + "$2");
}
return url;
}
const getNewURL = (url, targetObj) => {
let newURL = url;
Object.entries(targetObj).forEach(([key, value]) => {
newURL = replaceURLParams(newURL, key, value);
});
return newURL;
}
const testURL = "http://www.google.com/?token={token}&userId={userId}";
const urlParams = {
token: "abc123",
userId: "jack"
}
getNewURL(testURL, urlParams); // http://www.google.com/?token=abc123&userId=jack
经过我们的优化,目前这个方法可以满足以上几个需求:
- 新的参数添加进来不需要对原函数进行更改,有很好的兼容性
- 如果已有参数名称更改不会影响该函数运行及返回值,只要传入的参数名和URL已有的参数名匹配即可。有很好的独立性,减少依赖
- 只要循环调用就可以处理多个参数,而无需更改方法本身
方案讲解
创建正则
首先方法replaceURLParams中很重要的一点就是去定义正则;
new RegExp("\\b("+paramsName+"=).*?(#|&|$)");
我们知道声明一个正则,第一个参数可以是字符串,也可以是正则表达式。这里用的是一个字符串,不过是带有动态值(paramsName)的字符串。首先这个字符串含两对括号(),表示有两个捕获组。左边是个单词边界\b这个就没什么好说的了,然后我们看第一副括号里面的内容(paramsName=),如果参数是token的话相当于匹配"token="这样的字符串。然后第一个右括号)的右边紧跟着的是.*?。点(.)在这里的意思是匹配任意字符,星号代表的是可以匹配任意多个前面的字符,而问号?的一般意思是:匹配前面一个表达式 0 次或者 1 次,等价于 {0,1}。如果紧跟在任何量词 、 +、? 或 {} 的后面*,将会使量词变为非贪婪**(匹配尽量少的字符)。这里的?是属于第二种情况。所以当一遇到第二个括号里的内容时就会停止匹配。那么这里的.*?连在一起就相当于是匹配参数的占位符{token},但是它不在括号内,所以不会被捕获。第二个括号内容是匹配三个($,#,&)中的任意一个就会被捕获。
条件判断
接下来我们接着看函数里面的条件语句,这个没什么好说的,就是用来校验当前字符串符不符合我们定义的正则。
url.search(reg) >= 0
替换字符
最后一步是把匹配的字符用真正的值去做替换.
return url.replace(reg, "$1" + paramsValue + "$2");
首先我们看到替换的字符串中包含了两个特殊值,分别是$1和$2.(官网解释:$n 代表 插入第 n(索引从 1 开始)个捕获组,其中 n 是小于 100 的正整数),所以可以理解为现在用$1匹配到的字符内容去替换第一个捕获组,拿token举例就是token=替换paramasName=,然后就是paramsValue,这个就很好解释了就是拼接字符串。拼接完就是token=abc123。最后到了第二个捕获组$2,匹配到到是&,继续拼接字符串:token=abc123&。所以第一次替换完token后,我们得到的新URL应该长成这样:
http://www.google.com/?token=abc123&userId={userId}
以此类推,再去匹配一次userId就可以得到我们想要的完整的URL了
http://www.google.com/?token=abc123&userId=jack
结语
正则具有很强大地处理字符串的能力,尤其在字符替换和数据校验方面非常的好用,希望这篇文章能帮助到你。这里有个比较好用的网站,可以去测试你写的正则。码字不易,欢迎点赞收藏。