基于Koa实现轻量化服务引擎

前言

学习课程来自于抖音"哲玄前端"课程(《大前端全栈实践课》),从零开始做一个企业级的全栈应用框架。

一、elpis-core整体架构设计

elpis-core是基于koa实现的类似于egg的轻量化服务端。通过koa实例,将各种loader文件进行解析并挂载到Koa实例中。

1、BFF层结构

BFF(Backend For Frontend)翻译为为前端而生的后端,是一种架构设计,主要用于解决前后端数据协作及微服务框架的数据聚合等问题。

2、洋葱模型

概念

Koa 的中间件机制基于一个称为"洋葱模型"的设计模式。这种模式形象地将中间件的执行过程比喻为洋葱的层次。每个中间件都是洋葱的一层,能够在处理请求时进行操作,同时也可以在处理响应时进行操作。

洋葱模型的工作原理

在Koa中,每个中间件都可以进行以下操作:

  1. 请求处理:中间件在请求到达应用之前进行处理。
  2. 调用 next :通过 await next() 将控制权传递给下一个中间件,如果在中间件中没有执行next()的话,后续的中间件就不会执行了(一般做鉴权的时候,没通过就直接返回401,不执行next方法)。
  3. 响应处理:在下一个中间件完成处理后,继续执行当前中间件中的逻辑,进行响应处理。
中间件机制
中间件栈

Koa的中间件调用机制就像是一个函数链一样,每个中间件都可以对请求进行处理,然后通过 await next() 方式传递给下一个中间件,等到最后一个中间件执行完成,又会依次执行每个中间件await next() 后面的逻辑,这样每个中间件也都可以对响应进行处理。这样就像是洋葱一样,请求的执行顺序是中间件的注册顺序,但是响应的处理的顺序是(也就是 await next() 之后的处理逻辑)是刚好相反的。

3、设计原则

elpis-core遵循的原则(借鉴egg)是约定大于配置 ,有清晰的目录结构,按照一套统一的约定开发模式。同时采用模块化的设计,每个模块责任单一,互相独立,方便维护和扩展。

实现
js 复制代码
const path = require('path');
const Koa = require('koa');
const { sep } = path; // 为了兼容不同操作系统上的斜杠

const env = require('./env');

const configLoader = require('./loader/config');
const controllerLoader = require('./loader/controller');
const extendLoader = require('./loader/extend');
const middlewareLoader = require('./loader/middleware');
const routerLoader = require('./loader/router');
const routerSchemaLoader = require('./loader/router-schema');
const serviceLoader = require('./loader/service');

module.exports = {
  /**
   * 启动服务
   * @param {object} options 启动参数
   * options = {
   *  name: 应用名称
   *  homepage: 首页地址
   * }
   */
  start(options = {}) {
    // Koa实例
    const app = new Koa();
    // 应用的配置
    app.options = options;
    // 基础路径
    app.baseDir = process.cwd();
    // 业务文件路径
    app.businessPath = path.resolve(app.baseDir, `.${sep}app`);

    app.env = env(app);

    // 加载 configLoader
    configLoader(app);
    // 加载 extendLoader
    extendLoader(app);
    // 加载 middlewareLoader
    middlewareLoader(app);
    // 加载 routerSchemaLoader
    routerSchemaLoader(app);
    // 加载 serviceLoader
    serviceLoader(app);
    // 加载 controllerLoader
    controllerLoader(app);

    // 注册全局的中间件(必须在路由之前注册)
    try {
      require(path.resolve(app.businessPath, `.${sep}middleware.js`))(app);
    } catch (error) {
      console.error(error);
    }
    // 加载 routerLoader(必须在中间件之后)
    routerLoader(app);
    // console.log(app.routerSchema, app.middlewares, app.controller, app.service, app.config, '=======');
    try {
      const port = process.env.PORT || 8080;
      const host = process.env.HOST || '127.0.0.1';
      app.listen(port, host);
      console.warn(`Server is running at http://${host}:${port}`);
    } catch (error) {
      console.error(error);
    }
  },
};

以上引入的各个loader的目的其实就是加载对应的文件夹下的配置、扩展程序、中间件、服务、控制器、路由,并把它们分模块的挂载到Koa的实例对象上,这样使用时就直接从app实例中取。

加载顺序

通过 Loader 的加载顺序管理模块生命周期

  1. 环境配置加载

  2. 加载基础服务

    • 扩展程序
    • 中间件
    • 路由检验规则
    • 服务
    • 控制器
  3. 注册全局中间件

  4. 注册路由

  5. 启动应用

相关推荐
可我不想做饼干2 小时前
node.js是干啥的
node.js
运维开发王义杰4 小时前
nodejs:揭秘 npm 脚本参数 -- 的妙用与规范
前端·npm·node.js
Q_Q5110082856 小时前
python+uniapp基于微信小程序美食点餐系统
spring boot·python·微信小程序·django·flask·uni-app·node.js
苏琢玉6 小时前
作为 PHP 开发者,我第一次用 Go 写了个桌面应用
node.js·go·php
瓜瓜怪兽亚8 小时前
前端基础知识---10 Node.js(三)
数据结构·数据库·node.js
Q_Q51100828517 小时前
python+django/flask+uniapp基于微信小程序的瑜伽体验课预约系统
spring boot·python·django·flask·uni-app·node.js·php
该用户已不存在20 小时前
PHP、Python、Node.js,谁能称霸2025?
python·node.js·php
文心快码BaiduComate1 天前
再获殊荣!文心快码荣膺2025年度优秀软件产品!
前端·后端·代码规范
Q_Q5110082851 天前
python+nodejs+springboot在线车辆租赁信息管理信息可视化系统
spring boot·python·信息可视化·django·flask·node.js·php
濮水大叔1 天前
VonaJS多租户🔥居然可以同时支持共享模式和独立模式,太牛了🚀
typescript·node.js·nestjs