将 Koa 的洋葱模型应用于前端请求

556 阅读2分钟

用过 koa 的人都知道,洋葱模型是一种很巧妙的中间件模式,利用嵌套函数,它将响应和请求处理优雅封装在一个中间件中

Koa 中间件示例:

app.use(async (ctx, next) => {
  await next();
});

当我们处理请求时,是针对每一个请求做拆分,最小的关注点应是这个请求整体 (Request & Response),而不是将这个请求的 Request 和 Response 分别拆分在两个独立的逻辑中

最好我们能直接通过代码 syntax 层面直观的看出请求和响应的因果关系,而不是通过 function 调用顺序

比如使用 Koa 和 Axios 实现同样一个请求计时逻辑:

Koa

app.use(async (ctx, next) => {
  const start = Date.now()
  await next()
  const ms = Date.now() - start
  console.log(ms)
})

Axios

let start = 0

axios.interceptors.request.use(function(config){
  start = Date.now()
})

axios.interceptors.response.use(function(response){
  const ms = Date.now() - start
  console.log(ms)
})

在 axios 代码中,我们只是实现计时这一单一逻辑,却被强行拆分到两个 Hooks 中,单从 syntax 层面看不出他们有任何关系,只能创建一个外部变量来把它们耦合在一起

可以看出,洋葱 Middleware 和常规的 Hooks 比起来 ,前者看起来代码更简洁,逻辑也更内聚

洋葱模型在处理 Before / After 这种逻辑中有天然的优势,比如 vue-router、redux 也是使用这种方式,既然洋葱模型可以用在后端请求,那当然也可以用于客户端请求

所以,一个基于洋葱模型的 HTTP 客户端呼之欲出,它长这样:

Resreq

import Resreq from 'resreq'

const resreq = new Resreq({
  baseUrl: 'https://example.com'
})

// Intercepting responses and requests using middleware
resreq.use((next) => async (req) => {
  try {
    console.log(req) // Request can be changed here
    const res = await next(req)
    console.log(res) // Response can be changed here
    return res
  } catch (error) {
    console.log(error) // Catch errors here
    throw error
  }
})

const res = await resreq.get('/api', {
  params: { foo: 'bar' }
})

console.log(res)

源码:GitHub - molvqingtai/resreq: Fetch-based onion model http client.