逻辑构思
最近在 JD 开发自研 IDE 的时候遇到个乏味的问题,因为我的项目面向 iot 的独特性,有一部分接口是针对不同甲方订制的,那么天然不同的甲方所须要的性能,提供的接口(比方加密 sign 和 token 签名)天然也是不同的,以往的 ==「申请根封装 + 接口列表」== 的形式难以满足其灵便多变的需要,在解决单我的项目多交付并行的状况时也顾此失彼,故此从新设计了针对这类问题对于网络申请的黑箱化封装形式,无论 vue 还是 react 或是 node 环境都能够用。
首先咱们先确认要达到的指标:整个我的项目对于网络局部应该变成配置式的,并且一个甲方对应一个配置文件,配置文件要能间接配置或解决不同的 baseURL,headers 等定制服务,除此之外还该当可能反对向后兼容老我的项目,并保留原来常见的接口封装模式,放弃可读性和简直不须要学习老本
常见的接口封装形式如下
export async function getTest(params) { // 这里是一个简略 demo
const res = await request < ResponseParam > ({
url: '/getInfo/test',
method: 'POST',
data: params,
});
return res;
}
文件构造
设计出的文件构造大抵如下
src -
| ...
|- config
|- serve.ts(serve 外围)
|- serve
|-a.ts(甲方 a)
|-b.ts(甲方 b)
| ...
| ...
按照这个设计,如果之后有新的甲方须要交付,那么只需在 config/serve 文件夹下新增一个文件即可,既不会影响到其余交付,也便于对立配置。
Serve 外围封装
为了更便于介绍代码逻辑和波及的泛滥知识点,我会在这一章具体解释下是如何封装的,当然您能够边翻阅下一章的 UML 图边看这一章节来辅助了解(解说在代码上面)
serve.ts
import request from '../xxxxxxx/request'; // 这里是我的项目的申请根门路
// 默认值,这里除了 platform 之外也能够定制我的项目公共的一些配置
const defaultConfig = {
platform: 'default',
configuration: 'independent' // independent 独立配置,overall 整体配置
}
/** 这两个 interface 实际上应该写在里面,文章中写在这里了便于观看 */
interface magicType { // 这里须要写的十分具体
_base_url: {
release: string,
dev: string,
test: string,
},
_header: {
sign: Function,
// ...
}
}
interface encType {
platform: string,
configuration: string
}
class ServeCore {
platform: string; // 平台标识
request: any; // 魔改申请根
[prop: string]: any;
constructor(ENC:encType = defaultConfig) {
this.platform = ENC.platform;
(async () => {const export_list = await import(`./serve/${this.platform}`);
// [名称是 config] 整体配置
ENC.configuration === 'overall' && (this.request = this.magicRequest(export_list['config']));
for (let __function__ in export_list) {
// 魔改配置这里应该有两个判断规定?(依照名字辨别,依照类型辨别)if (typeof export_list[__function__] === 'function' || __function__ !== 'config') {// 这里是须要注册到租用与的接口函数,[是 function 类型 || 名称不是 config]
this[__function__] = export_list[__function__].bind({request: this.request})
} else {// [既不是 function 类型,名称又叫 config] 独立配置
ENC.configuration === 'independent' &&
(this.request = this.magicRequest(export_list[__function__]));
}
}
console.log('========== this', this);
delete this.then;
return this
})();}
// 魔改 request 的办法
magicRequest(config: magicType) {
// 这里的做法是把配置梆到 request 的全局
return request.bind({config})
}
}
export {
ServeCore, // 这里是用来从新自定义配置的
magicType,
encType
}
export default new ServeCore
首先咱们定义了一个名叫 ServeCore 的 class,其中 platform 是平台标识(甲方),用来辨别不同的平台;request 是解决后的封装申请根,这个实际上是能够始终在变的,后文拓展会讲到。
按需加载文件
在构造函数中咱们须要把指标甲方平台对应的文件按需动静引入到这个 calss 中并进行解决,然而很遗憾 js 中的 class 结构器目标是返回结构后的实例的,不反对期待异步操作(因为那样的话返回的将是一个 promise 对象,这里在运行时会报错),但侥幸的是,咱们晓得 js 的 class 只是一个 function 的语法糖,底层还是 function 实现,所以我在构造函数中写了这样一个构造:
constructor(ENC:encType = defaultConfig) {(async () => {
// ...
delete this.then;
return this
})();}
目标是立刻执行一个异步箭头函数作为返回值, 实际上返回的是一个 Promise 实例, 这个 Promise 在 resolve 时才会将创立的对象实例 (this) 返回, 于是咱们内部的 await 就失去了实现异步结构后的实例。奇妙的绕过了 constructor 的机制
而接下来 await import 把不同甲方对应的文件引入进行解决,上面是举例的一个 demo
a.ts(甲方 a)
// 配置 应该放在第一个?export const config = {
// TODO: 以后须要探讨不过集体认为配置项应作为间接字面量,而不应该是一个函数
_base_url: {
release: 'https://xxx.com',
dev: 'https://xxx.com',
test: 'https://xxx.com',
},
_header: {sign: (param) => { // 这里实际上的调用环境是在 request 中,所以是函数更好?// ...
}
}
}
// 这里是一个简略 demo
export async function getTest(params) {
const res = await request < ResponseParam > ({
url: '/getInfo/test',
method: 'POST',
data: params,
});
return res;
}
// ...
== 有事件先去忙了过会儿回来补一下,这里还有很多货色没写分明 ==
xxx.tsx
import Service from '../../config/serve'
// ...
// 某个函数内:
const res = await Service.getTest(param);console.log(res)
// 这里 ide 会报错但其实能够失常运行的
图形化了解(如果有趣味能够搞一份思维导图)
UML 图如下
sequenceDiagram
接口文件(a.js) ->>+ ServeCore: config(配置)和接口办法
ServeCore->>+request(封装申请根): config(配置)request(封装申请根)->>+ 接口文件(a.js): 魔改后的 request
接口文件(a.js) ->>+ ServeCore: bind 魔改 request 后的接口办法
ServeCore->>+ xxx.tsx(个别页面调用): 网络接口办法
Note right of request(封装申请根): 齐全解决好的接口
相干链接
参考文献:https://www.blackglory.me/asy…