有一个很简单的需求以下:javascript
给你一段URL,在其中插入一些参数,并返回新的URL,如何实现?html
这里的参数,有时候称为query
,有时候称为params
,通常称为search
,指的是vue
http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument
java
中的key1
和key2
。ios
最近正在对接阿里云的金融级实人认证,在传递认证成功回调页时就遇到了这样一个问题。vue-router
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的值是这样的:axios
http://taobao.com/?c=3&d=4
最终结果就是后端
http://taobao.com/?c=3&d=4?a=1&b=2
将这段URL中的search解析,获得的结果是api
{ c: '3', d: '4?a=1', b: '2', }
显然不符合预期。ui
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后面拼接字符串是不行的。
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,则链接符改用&
。
相关连接: