基于koa二次封装的服务端框架--elpis-core

注:引用抖音"哲玄前端",《全栈实践课》

前言

elpis-core 是一个基于 Koa.js 的应用框架,其核心目标是通过模块化、分层架构和设计模式的运用,为开发者提供高效、灵活且可维护的开发体验。它致力于解决复杂项目开发中的常见问题,如模块耦合、代码难以维护、功能扩展困难等。

项目整体架构设计

模块化设计思维

将应用划分为多个功能模块,如 config、extend、middleware、routerSchema、controller、service 等,每个模块有明确的职责和边界。这种设计便于团队协作,不同开发人员可以专注于各自擅长的模块,同时降低修改一个模块对其他模块的影响。

分层架构原则

遵循分层原则,将应用分为配置层、扩展层、中间件层、路由模式层、控制器层、服务层等。各层之间有清晰的接口定义,上层依赖下层,但下层不依赖上层,提高了系统的可维护性和可扩展性。

该框架使用了一系列的 loader来加载并挂载到 Koa 实例,每个 Loader 有明确的功能描述和关键技术点
  • configLoader(配置管理模块)
  • middlewareLoader(中间件加载器)
  • extendLoader(扩展加载)
  • routerSchemaLoader(接口参数校验)
  • serviceLoader(服务加载)
  • controllerLoader(控制器)
  • routerLoader(路由加载器)
config配置管理模块

通过不同环境配置文件(如 config.beta.jsconfig.prod.js),实现一键切换环境,满足开发、测试、生产等不同场景需求。config.default.js 作为基础配置,其他环境配置可在此基础上覆盖,平衡默认行为与灵活性。

  • configLoader 核心代码
js 复制代码
// 找到 config/ 目录
  const configPath = path.resolve(app.baseDir, `.${sep}config`);

  // 获取 default.config
  let defaultConfig = {};
  try {
    defaultConfig = require(path.resolve(configPath, `.${sep}config.default.js`));
  } catch (e) {
    console.log('[exception] there is no default.config file');
  }

  // 获取env.config
  let envConfig = {};
  try {
    if (app.env.isLocal()) { // 本地环境
      envConfig = require(path.resolve(configPath, `.${sep}config.local.js`));
    } else if (app.env.isBeta()) { // 测试环境
      envConfig = require(path.resolve(configPath, `.${sep}config.beta.js`));
    } else if (app.env.isProduction()) { // 生产环境
      envConfig = require(path.resolve(configPath, `.${sep}config.prod.js`));
    }
  } catch (e) {
    console.log('[exception] there is no default.config file');
  }
  // 覆盖并加载 config 配置
  app.config = Object.assign({}, defaultConfig, envConfig);
middleware中间件模块

灵活加载中间件并构建洋葱圈模型

  • middlewareLoader核心代码
js 复制代码
fileList.forEach(file => {
    // 提取文件名称
    let name = path.resolve(file);

    // 截取路径 app/middleware/custom-module/custom-middleware => custom-module/custom-middleware
    name = name.substring(name.lastIndexOf(`middleware${sep}`) + `middleware${sep}`.length, name.lastIndexOf('.'));

    // 把 '-' 统一改为驼峰式,custom-module/custom-middleware.js => customModule.customMiddleware
    name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());

    // 挂载 middleware 到内存 app 对象中
    let tempMiddleware = middlewares;
    const names = name.split(sep);
    for (let i = 0, len = names.length; i < len; ++i) {
      if (i === len - 1) {
        tempMiddleware[names[i]] = require(path.resolve(file))(app);
      } else {
        if (!tempMiddleware[names[i]]) {
          tempMiddleware[names[i]] = {};
        }
        tempMiddleware = tempMiddleware[names[i]];
      }
    }
  });
extend扩展功能

用于拓展应用的额外能力,例如日志

  • extendLoader核心代码
js 复制代码
// 遍历所有文件目录,把内容加载到 app.extend 下
  fileList.forEach(file => {
    // 提取文件名称
    let name = path.resolve(file);

    // 截取路径 app/extend/custom-extend => custom-extend
    name = name.substring(name.lastIndexOf(`extend${sep}`) + `extend${sep}`.length, name.lastIndexOf('.'));

    // 把 '-' 统一改为驼峰式,custom-extend.js => customExtend
    name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());

    // 过滤 app 已经存在的key
    for (const key in app) {
      if (key === name) {
        console.log(`[extend load error] name:${name} is already in app`);
        return;
      }
    }

    // 挂载 extend 到内存 app 对象中
    app[name] = require(path.resolve(file))(app);
  });
routerSchem路由模式定义

定义路由模式,便于生成标准路由配置,减少重复定义,同时为参数校验等提供依据

  • routerSchemaLoader核心代码
js 复制代码
// 注册所有 routerSchema ,使得可以 'app.routerSchema' 这样访问
  let routerSchema = {};
  fileList.forEach(file => {
    routerSchema = {
      ...routerSchema,
      ...require(path.resolve(file))
    }
  });
  app.routerSchema = routerSchema;
service服务加载器

提供业务逻辑,供 controller 调用

  • serviceLoader核心代码
js 复制代码
fileList.forEach(file => {
    // 提取文件名称
    let name = path.resolve(file);

    // 截取路径 app/service/custom-module/custom-service => custom-module/custom-service
    name = name.substring(name.lastIndexOf(`service${sep}`) + `service${sep}`.length, name.lastIndexOf('.'));

    // 把 '-' 统一改为驼峰式,custom-module/custom-service.js => customModule.customService
    name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());

    // 挂载 service 到内存 app 对象中
    let tempService = service;
    const names = name.split(sep); // [customModule(目录), customService(文件)]
    for (let i = 0, len = names.length; i < len; ++i) {
      if (i === len - 1) { // 文件
        const serviceModule = require(path.resolve(file))(app);
        tempService[names[i]] = new serviceModule();
      } else { // 文件夹
        if (!tempService[names[i]]) {
          tempService[names[i]] = {};
        }
        tempService = tempService[names[i]];
      }
    }
  });
  app.service = service;
controller控制器

控制器层,处理 HTTP 请求并调用服务层

  • controllerLoader核心代码
js 复制代码
// 遍历所有文件目录,把内容加载到 app.controller 下
  const controller = {};
  fileList.forEach(file => {
    // 提取文件名称
    let name = path.resolve(file);

    // 截取路径 app/controller/custom-module/custom-controller => custom-module/custom-controller
    name = name.substring(name.lastIndexOf(`controller${sep}`) + `controller${sep}`.length, name.lastIndexOf('.'));

    // 把 '-' 统一改为驼峰式,custom-module/custom-controller.js => customModule.customController
    name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());

    // 挂载 controller 到内存 app 对象中
    let tempController = controller;
    const names = name.split(sep); // [customModule(目录), customController(文件)]
    for (let i = 0, len = names.length; i < len; ++i) {
      if (i === len - 1) { // 文件
        const ControllerModule = require(path.resolve(file))(app);
        tempController[names[i]] = new ControllerModule();
      } else { // 文件夹
        if (!tempController[names[i]]) {
          tempController[names[i]] = {};
        }
        tempController = tempController[names[i]];
      }
    }
  });
  app.controller = controller;
router路由加载器

路由定义,映射对应的controller

  • routerLoader核心代码
js 复制代码
// 注册所有路由
  const fileList = glob.sync(path.resolve(routerPath, `.${sep}**${sep}**.js`));
  fileList.forEach(file => {
    require(path.resolve(file))(app, router);
  });
  // 路由注册到 app 上
  app.use(router.routes());
  app.use(router.allowedMethods());

总结

本项目通过 elpis-core 核心框架,实现了环境配置管理、路由模式定义、中间件链式调用等功能,为业务开发提供了坚实的基础。同时,项目结构清晰,符合大型项目的开发规范,具有良好的扩展性和可维护性。

相关推荐
Kx…………4 分钟前
Uni-app入门到精通:subPackages节点为小程序的分包加载配置
前端·学习·小程序·uni-app
齐尹秦1 小时前
HTML5 新的 Input 类型学习笔记
前端·html5
还是鼠鼠2 小时前
Node.js 如何发布一个 NPM 包——详细教程
前端·vscode·npm·node.js
kiro_10232 小时前
【ESP32S3】esp32获取串口数据并通过http上传到前端
前端·网络协议·http
鎏年_4 小时前
Vue2项目打包后,某些图片被转换为base64导致无法显示
前端·javascript·vue.js
康6205 小时前
vue2中引入elementui
前端·javascript·elementui
听风说雨的人儿5 小时前
ElementUI时间选择、日期选择
前端·javascript·elementui
wfsm7 小时前
React多层级对象改变值--immer
前端·javascript·react.js
沐土Arvin7 小时前
Chrome Performance 面板完全指南:从卡顿到丝滑的终极调试术
前端
少年姜太公8 小时前
一个半小时的腾讯一面,人麻了
前端·javascript·面试