记在实战项目中对于请求模块的无敌封装
分类:javascript
这是我参与更文挑战的第3天,活动详情查看: 更文挑战
困扰
遇到这个项目时,查看老代码发现很多页面上的请求方法非常复杂。
- 获取浏览器缓存中的路由对象,轮询找到需要请求的别名路由对象。
- 通过函数传入路由参数,并返回需要请求的
url
。 - 接着使用函数传入
url
和body
参数。 - 最后获取到服务器返回的值。
//第一步传入路由参数,获取到url
const url = getApiUrl("user.edit.name", {
user_id: this.user_id
})
//第二步创建body参数
const httpObj = {name: 'Kev1nzh'}
//第三步结合url和参数,请求返回结果。
const result = http.get(url, httpObj).subscribe(res => {
...do something
})
这样的做法其实非常正确,一步步单独获取正确的数据,但是我们能不能优化一下呢,最好是一行代码就可以解决上述的代码。
分析
浏览器缓存中存在一个路由对象。如下代码表述:
路由对象下的子元素以alias
为key
,路由数据
为value
。
routeObj: {
base: {
'user.task': [
{
alias: 'user.task',
method: 'get',
module_name: 'base',
url: 'api/base/user/task'
}
],
'user.edit.name': [
{
alias: 'user.edit.name',
method: 'get',
module_name: 'base',
url: 'api/base/user/edit/name/{user_id}'
}
],
},
login: {
....
},
.....
}
思考了优化的逻辑, 如果想要以最少的代码来请求并保证功能完整,应该如下。
async editUserName(name: string, user_id: sumber) {
//user_id为路由参数,name为body参数。
const result = await fetch('user.edit.name', {user_id, name})
...do something
}
传入参数分为alias
路由别名和httpObj
不管传入的是路由参数还是body参数都可以放在一个对象内。
所以我们的处理逻辑:
- 从浏览器缓存中获取路由对象,根据
alias
路由别名获取路由对象。 - 如果没有路由参数则直接返回请求,如果有就区分参数并保存,
- 最后分别传入两种不同的参数,最后返回请求。
需要的函数为:
getRoutesParams
: 通过传入的url
来获取路由参数getParamsPosition
: 递归获取url
的参数,结合上面的函数。fetchPost
: 统一所有的数据开始请求并返回结果。getQueryObject
: 根据对象或数组返回query string
。fetch
: 主函数。
实现
//首先我们根据逻辑写几个需要的函数。
/**
* 获取路由所需参数
* @param {string} url
* @return {array} 路由参数数组
**/
getRoutesParams(url: string): string[] {
let params = []
getParamsPosition(url, params)
return params
}
/**
* 根据url获取
* @param {string} url
* @param {array} 传入的路由参数数组
* @return {array} 传入的路由参数数组
**/
getParamsPosition(string, array) {
// 获取url中参数的位置
// 例: api/base/user/edit/{team_id}name/{user_id}
let startP = string.indexOf("{")
let endP = string.indexOf("}")
let param = string.slice(startP + 1, endP)
array.push(param)
// 因为路由参数的数量是不定的,所以这边需要轮询至没有参数为止
string = string.replace(string.slice(startP, endP + 1), "")
if (string.includes("{") && string.includes("}")) {
getParamsPosition(string, array)
} else {
return array
}
}
/**
* 减少相同代码,统一请求接口
* @param {string} aclString 权限acl
* @param {object} routeHttpObj 路由别名参数
* @param {string} method 请求方法
* @param {object} postHttpObj post参数
* @param {object} paramsHttpObj 路由后缀参数
* @return {Promise<Response>} result 请求接口后的返回参数
**/
async fetchPost(
aclString: string,
routeHttpObj: any,
method: string,
postHttpObj: any,
paramsHttpObj: any
): Promise<any> {
let httpUrl: any = Object.keys(routeHttpObj).length
? await getApi(aclString, routeHttpObj)
: await getApi(aclString)
//处理有路由后缀参数:/api/base/user/edit?user_id={user_id}&name={name}
if (paramsHttpObj && Object.keys(paramsHttpObj).length) {
const paramsUrl: string = getQueryObject(paramsHttpObj)
httpUrl = `${httpUrl}?${paramsUrl}`
}
const result = await .http[method](httpUrl, postHttpObj).toPromise()
return result
}
/**
* url + '?' + result
* 通过此函数转译成query参数 result
* @params {object} 需要转译的对象
* @return {string} 转译后的参数string page_size=20&page=1&search[acquisition][student][0]=10&
**/
getQueryObject(obj): string {
let getPairs = (obj, keys = []) =>
Object.entries(obj).reduce((pairs, [key, value]) => {
if (typeof value === "object") pairs.push(...getPairs(value, [...keys, key]))
else pairs.push([[...keys, key], value])
return pairs
}, [])
let x = getPairs(obj)
.map(([[key0, ...keysRest], value]) => `${key0}${keysRest.map((a) => `[${a}]`).join("")}=${value}`)
.join("&")
return x
}
需要的函数都已经搞定了,正式开写主函数,
/**
* 简单封装
* @params {string} 路由别名
* @params {Object} 传入参数
* @return Response
**/
async fetch(aclString: string, httpObj?: Object, paramsObj?: Object) {
let paramsSuccess = true
const acl: any = await getAcl(aclString) // 根据路由别名获取路由对象
const { url, method }: { url: string; method: string } = acl
const isHaveRouteParams: boolean = acl.url.includes("{") && acl.url.includes("}")
// 首先 根据别名获取路由数据,然后判断是否有路由参数
if (isHaveRouteParams) {
// 创建路由参数、body参数、路由所需参数
let [routeHttpObj, postHttpObj, routesArray]: [any, any, string[]] = [{}, {}, getRoutesParams(url)]
// 根据路由所需参数来填入
for (let route of routesArray) {
if (httpObj[route]) {
routeHttpObj[route] = httpObj[route]
} else {
paramsSuccess = false
}
}
// 不是路由参数的 params
for (const key in httpObj) {
if (httpObj.hasOwnProperty(key)) {
const element = httpObj[key]
if (!routesArray.includes(key)) {
postHttpObj[key] = element
}
}
}
if (paramsSuccess) {
const result = await fetchPost(aclString, routeHttpObj, method, postHttpObj, paramsObj)
return result
} else {
console.error(routesArray)
console.error("参数传入不全或者参数value为空。")
}
} else {
// 没有路由参数直接请求
const result = await fetchPost(aclString, {}, method, httpObj, paramsObj)
return result
}
}
打完收工。
最后
这是一个项目中真实的案例,自从我封装完后,大家似乎都逐渐改成了上述的方法来请求接口获取数据。对于程序方面的热情就是来源于这些优化后的反馈哈哈。
好哥哥学到的话点个赞再走吧。
另外推荐下新写的Vite
源码解析,点我本站跳转
查看线上文档体验更佳 查看文档 Powered by dumi
看完有帮助的可以进入github给我一个?小星星 谢谢!