有一个很简略的需要如下:
给你一段URL,在其中插入一些参数,并返回新的URL,如何实现?
这里的参数,有时候称为query
,有时候称为params
,个别称为search
,指的是
http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument
中的key1
和key2
。
最近正在对接阿里云的金融级实人认证,在传递认证胜利回调页时就遇到了这样一个问题。
第一层: 间接拼
import qs from 'query-string'
function resolve (url) {
const params = qs.stringify({ a: 1, b: 2 }) // a=1&b=2
return url + '?' + params
}
理论业务中,90%的场景这样写没问题,但如果url的值是这样的:
http://taobao.com/?c=3&d=4
最终后果就是
http://taobao.com/?c=3&d=4?a=1&b=2
将这段URL中的search解析,失去的后果是
{
c: '3',
d: '4?a=1',
b: '2',
}
显然不合乎预期。
第二层: 兼容已存在的search
import qs from 'query-string'
function resolve (url) {
const params = qs.stringify({ a: 1, b: 2 }) // a=1&b=2
return url + url.includes('?') ? '&' : '?' + params
}
看上去问题仿佛解决了,很多人也只思考到这一层,但当初还有这样一种url:
http://taobao.com/#/xxx
尤其是单页利用,这个模式的hash路由十分常见。
如果只是简略地拼接到URL尾部:
http://taobao.com/#/xxx?a=1&b=2
将这段URL中的search解析,失去的后果是
{}
能够看到拼接的参数基本没跑到search外面去。
也就是说,只有URL中呈现了#
,这之后呈现的?
就不会被视作search的起始标记,而是hash的一部分。
如果URL再简单一点,比方:
http://taobao.com/?c=3&d=4#/xxx
下面的拼接会变成:
http://taobao.com/?c=3&d=4#/xxx&a=1&b=2
岂但没有依照预期插入参数,还毁坏了本来的hash构造。
实际上,以vue-router为例,它的路由零碎中恰好就用到了hash中的?
。
比方:
http://example.com/user/:foo/info?c=3&d=4#/xxx?a=1&b=2
在vue-router里,xxx
是路由的path,foo
被称作params,a
和b
被称作query,别离能够通过route.params
和route.query
获取。
#
之后能够接任意字符串,?a=1&b=2
只是路由本人定义的一套外部规定,为路由传参服务。
而c
和d
才是search,须要从location.search
中解析。
如果后端接管了这样一段GET申请,hash前面的货色都会被摈弃,只有search能够被接管和解析。
因而,插入参数不能呈现在#
之后,也就是说,简略地在URL前面拼接字符串是不行的。
第三层: 只解决search
import qs from 'query-string'
function resolve (url) {
const params = qs.stringify({ a: 1, b: 2 }) // a=1&b=2
const urlObj = new URL(url)
urlObj.search += urlObj.search.startsWith('?') ? '&' : '?' + params
return urlObj.href
}
这样就能够解决下面的简单状况了。
应用URL对象是一个讨巧的方法,将url字符串解析为URL对象后,能够只批改它的search属性而不影响其余局部。
在axios
的源码中,咱们能够看到另一种规范实现:
function buildURL(url, params) {
if (!params) {
return url;
}
// params序列化过程略
var hashmarkIndex = url.indexOf('#');
if (hashmarkIndex !== -1) {
url = url.slice(0, hashmarkIndex);
}
url += (url.indexOf('?') === -1 ? '?' : '&') + params;
return url;
};
思路很简略,就是把序列化后的params插入到URL的开端,但若存在hash,则插到hash之前,若存在search,则连接符改用&
。
相干链接:
What is a URL
URL对象
Vue-router 路由对象
发表回复