逻辑构思
最近在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中,所以是函数更好? // ... } }}// 这里是一个简略demoexport 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...