20行代码实现【洋葱模型】,其实没那么难理解

作者: jie 分类: JavaScript 发布时间: 2023-09-25 09:44

洋葱模型

洋葱模型是什么呢?作用是什么呢?

先说说他的作用吧,他的作用是为了对一个请求的整个生命周期做一个统一的管理,一个请求的生命周期包括请求、响应。由于实现方式像一层一层剥开洋葱,所以叫洋葱模型~

举例子

可能很多人会觉得一脸懵逼?不知道我在表述什么?那我通过两个例子来说明,大家可能就比较清晰了~

Axios 拦截器

相信很多人在项目中都用过 Axios 的拦截器

  • 请求拦截器
  • 响应拦截器
axiosInstance
.interceptors
.request.use((config) => {
  console.log(config) // {}
  config.name = 'sunshine_lin'
}

axiosInstance
.interceptors
.response.use((res) => {
  console.log(res.config)
  // { name: 'sunshine_lin' }
}

在每次请求中,请求拦截器和响应拦截器中,获取到的config是相通的,也就是同一个对象。

我们甚至可以多个拦截器

// 请求拦截器1
axiosInstance
.interceptors
.request.use((config) => {
  console.log(config) // {}
  config.name = 'sunshine_lin'
  
  return config
}

// 请求拦截器2
axiosInstance
.interceptors
.request.use((config) => {
  console.log(config)
  // { name: 'sunshine_lin' }
  config.age = 20
  
  return config
}

// 响应拦截器1
axiosInstance
.interceptors
.response.use((res) => {
  console.log(res.config)
  // { name: 'sunshine_lin',
  //   age: 20
  // }
  res.data = 1
  
  return res
}

// 响应拦截器2
axiosInstance
.interceptors
.response.use((res) => {
  console.log(res.data) // 1
  
  return res
}

其实基础比较好的同学,可以想象出他的实现原理基本(伪代码)就是:

// 伪代码

// 收集
const tasks = []
const use = (fn) => {
  tasks.push(fn)
}

// 执行
const config = {}
tasks.forEach(fn => {
  config = fn(config)
})

Koa

Koa 是一个 Nodejs 的框架,他也有洋葱模型的实践,比如:

const koa = new Koa()

koa.use(async (ctx, next) => {
  console.log(1)
  console.log(ctx) // {}
  await next()
  console.log(ctx) 
  // { name: 'sunshine_lin', age: '20' }
  console.log(2)
})

koa.use(async (ctx, next) => {
  console.log(3)
  ctx.name = 'sunshine_lin'
  await next()
  console.log(4)
})

koa.use(async (ctx, next) => {
  console.log(5)
  ctx.age = '20'
})

koa.listen(3000)

执行的结果为:

就感觉是,遇到 next 就一层一层往两边掰开。

简单实现洋葱模型

function action(instance, ctx) {
  // 记录索引
  let index = 1

  function next() {
    // 记录执行的中间件函数
    const nextMiddleware = instance.middlewares[index]
    if (nextMiddleware) {
      index++
      nextMiddleware(ctx, next)
    }
  }
  // 从第一个开始执行
  instance.middlewares[0](ctx, next)
}


class Koa {
  middlewares = []
  use(fn) {
    this.middlewares.push(fn)
  }
  listen(port) {
    Promise.resolve({}).then((ctx) => {
      action(this, ctx)
    })
  }
}

发表回复