需求分析
首先,我們可能需要使用我們封裝的axios去發送一個下面這樣的簡單請求
axios({
method: 'get',
url: '/api/getInfo',
params: {
a: 1,
b: 2
}
})
最終,我們希望我們發送的請求url是這樣的,/api/getInfo?a=1&b=2,這樣服務器就可以通過請求的url解析到我們傳來的參數了。那么,我們要做的實際上就是把params的key和vaule拼接到url上,當然,params是很負責的,可能會有以下幾種情況:
參數為數組
axios({
method: 'get',
url: '/api/getiInfo',
params: {
foo: ['bar', 'baz']
}
})
最終請求的url是這樣的:/api/getInfo?foo[]=bar&foo[]=baz
參數為對象
axios({
method: 'get',
url: '/api/getiInfo',
params: {
foo: {
bar: 'baz'
}
}
})
最終請求的url是這樣的: /api/getInfo?foo=%7b%22bar:%22baz%2z%7d,foo后面拼接的是{"bar": "baz"}encode后的結果
參數為Date對象
axios({
method: 'get',
url: '/api/getiInfo',
params: {
date
}
})
最終請求的url是/api/getInfo?date=2019-04-01T05:55:39.030Z,date后面跟的是date.toISOString()的結果
特殊字符的支持
對於@、:、$、[、]、空格,我們是允許出現在url中的,不希望encode
axios({
method: 'get',
url: '/api/getiInfo',
params: {
foo: '@:$'
}
})
最終請求的url是這樣的:/api/getInfo?foo=@:$+,注意我們會把空格轉換成+
空值忽略
對於值為null或者undefined的屬性,我們是不會添加到url參數中的。
axios({
method: 'get',
url: '/api/getiInfo',
params: {
foo: 'bar',
baz: null
}
})
最終請求的url是:/api/getInfo?foo=bar
丟棄url中的哈希標記
axios({
method: 'get',
url: '/api/getInfo#hash',
params: {
foo: 'bar'
}
})
最終請求的url是:/api/getInfo?foo=bar
保留url中已存在的參數
axios({
method: 'get',
url: '/api/getiInfo?foo=bar',
params: {
bar: 'baz'
}
})
最終請求的url是:/api/getInfo?foo=bar&bar=baz
buildUrl的實現
根據上面我們的分析,接下來我們來實現一個工具function,然后把params拼接到url上,並能處理上面這幾種params
// helpers/util.ts
const toString = Object.prototype.toString
export function isDate(val: any): val is Date {
return toString.call(val) === '[object Date]'
}
export function isObject(val: any): val is Object {
return val !== null && typeof val === 'object'
}
export function isPlainObject(val: any): val is Object {
return toString.call(val) === '[object object]'
}
// helpers/url.ts
import { isDate, isPlainObject } from './util'
import { encode } from 'punycode';
export function buildUrl(url: string, params?: any): string {
if (!params) {
return url
}
const parts: string[] = []
Object.keys(params).forEach(key => {
const val = params[key]
if (val === null || typeof val === 'undefined') {
return // 結束本次forEach
}
let values = []
if (Array.isArray(val)) {
values = val
key += '[]'
} else {
values = [key]
}
values.forEach(val => {
if (isDate(val)) {
val = val.toISOString()
} else if (isPlainObject(val)) { // 普通對象,不包含formData類型等
val = JSON.stringify(val)
}
parts.push(`${encode(key)}=${encode(val)}`)
})
})
let serialzedParams = parts.join('&')
if (serialzedParams) {
const markIndex = url.indexOf('#')
if (markIndex !== -1) {
url = url.slice(markIndex, 1)
}
url += (url.indexOf('?') === -1 ? '?' : '&')
}
return url
}