持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的30天,点击查看活动详情
一、 背景 & 前情回顾
上一篇文开始着手实现我们前面的诉求——一款校验接口参数类型的工具。本文目前算是详述了一下这个工具的架构设计,目前包含以下几方面:
-
RuleSet 类的设计以及类型上的 exec/addErrHandler/exec 方法;
-
工具设想已经完成的请情况下,设计如何使用这个工具;
-
设计工具可能用到的类型校验失败时对应的错误以及生成错误对象的方法及错误提示信息设计;
本篇开始讨论代码如何实现;
二、代码实现
上一篇中我们一共设计了四个方法:constructor 构造函数、exec、doInterceptor、addErrHandler。当然,我们这个工具的方法不只这几个,随着工具的深入,我们的方法也会持续增多,从这里开始我们逐个实现他们。
2.1 构造函数
- 方法参数:
-
- xfetch: 网络库对象,如果使用 axios 就传递 axios 对象就可以,如果是自定义网络库,请确保支持 xfetch.interceptors.request.use(fn) 的语法;
-
- schema: 接口参数的 schema 对象,其中
schema['*']
表示公参;
- schema: 接口参数的 schema 对象,其中
-
- 方法作用:RuleSet 构造函数,在构造函数中主要进行以下工作:
- this.commonParams,单独从 schema 中获取
schema['*']
表示的公参,之所以单独保存是因为有单独用处; - this.schema 缓存 schema 对象;
- this.errors 数组,保存本次检查所有的错误信息;
- this.queue 数组,用于保存各次请求的配置信息,将配置信息加入到一个队列中;
- this.utils 对象,其中包含了一些内置的方法;
- 调用 this.doInterceptor 方法并传入 xfetch,这个方法就会向 xfetch.interceptors.request 中加入一个拦截器;
- this.commonParams,单独从 schema 中获取
class RuleSet {
constructor (xfetch, schema) {
// 全局参数
this.commonParams = schema['*']
this.schema = schema
this.errors = []
this.queue = []
this.utils = {
getTypeOf: this.getTypeOf.bind(this)
}
this.doInterceptor(xfetch)
}
}
2.2 RuleSet.prototype.addErrHandler
方法参数:错误处理 handler 函数;
方法作用:用于处理接口参数校验失败时的错误信息的函数,该函数接收一个参数,该参数包含了本次校验时收到的错误信息;
注意:这个方法是个可选的,默认情况下,我们设计了 console.error 的方式将本次收集到的错误信息抛出到控制台;如果你有更为特殊的诉求,就必须在这个 handler 中进行自定义的方式处理了。其实这种处理方式也是一种常规的处理思路:设计默认+支持自定义;
class RuleSet {
constructor (xfetch, schema) {}
addErrHandler (handler) {
if (typeof handler !== 'function') {
return console.error('the addErrHandler is not a function, fallback handler to console.error!')
}
this.errorHander = handler
}
}
2.3 RuleSet.prototype.doInterceptor
-
方法参数:xfetch 对象,即网络库对象,再次强调支持 xfetch.interceptors.request.use(fn) 的语法!
-
方法作用:通过向网络库中注册一个新的请求拦截器实现获取请求配置信息,并进行校验,详情如下:
- 判断 xfetch 并且 xfetch.interceptors.interceptors 且 xfetch.interceptors.interceptors.request 且 xfetch.interceptors.interceptors.request.use 是否存在,如果有个不满足则认定该网络库不能使用这个工具并抛出异常信息到 console.error,然后退出;
- 能走到这里,说明是支持 xfetch.interceptors.request.use 语法的,此时向 xfetch 的拦截器中注册新的拦截器函数;
class RuleSet {
constructor (xfetch, schema) {
this.doInterceptor(xfetch)
}
doInterceptor (xfetch) {
if (!xfetch
|| !xfetch.interceptors
|| !xfetch.interceptors.request
|| typeof xfetch.interceptors.request.use !== 'function'
) return console.error('no Xfetch instance or xfetch.interceptor is not defefined !')
// 注册新的拦截器
xfetch.interceptors.request.use((cfg) => {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
this.queue.forEach(itm => this.exec(itm))
this.queue.length = 0
if (this.errors.length) {
this.errorHander ? this.errorHander(this.errors) : console.error(this.errors)
}
this.errors = []
timer = null
}, 1000)
// add into a queue for two factors
// 1. this interceptor added too early to get params later added
// 2. better performance, do not block xfetch request
this.queue.push(cfg)
return cfg
})
}
}
2.3.1 拦截器细节
关于上面的拦截器,我们需要仔细讨论一下,首先分析一下其中的具体逻辑,再说为你什么这么设计:
- 可以很直观的看到,这里有一个节流的操作,即通过 timer 控制 1s 以内的各请求的 config 对象都会被添加到队列 this.queue 中,思考一个问题为什么这么做?;
- 相当于 1s 内没有请求时在进行校验,故而具体的校验逻辑放到了节流用的定时器内,而校验就是遍历 this.queue,调用 RuleSet.prototype.exec 方法并传入每次请求的 config 对象;
三、总结
本篇小作文开始实现我们的 RuleSet 类型,逐步分析每个步骤的要求,目前主要讲述了以下方法:
- 构造函数,初始化实例属性,调用 RuleSet.prototype.doInterceptor 方法注册拦截器;
- RuleSet.prototype.addErrHander,兜底 console.error 抛出异常,支持自定义的方式;
- RuleSet.prototype.doIntercetpor 注册的拦截器收集每次请求时的 config 对象的引用到 this.queue,网络空闲时进行校验;