有一個很簡單的需求以下: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,則鏈接符改用&
。
相關連接: