个人学习笔记总结(一)基于koa的elpis-core解析引擎的设计与实现(elpis全栈项目)

141 阅读3分钟

前言

elpis是一个企业级应用框架,旨在能够解决日常开发中80%的重复CRUD操作,以及20%的定制开发需求,提高开发效率,本文将简单介绍elpis中的服务端解析引擎——eplis-core

elpis-core的架构设计

模块化设计
  • elpis-core将应用中各个功能模块(中间件、环境配置、控制器等)分离成不同的loader来解析和加载,每个loader负责特定的模块解析和加载,确保模块的职责清晰。
约定优于配置
  • 通过约定的目录结构和文件命名,减少配置的复杂性。例如,控制器文件放在 app/controller 目录下,配置文件放在 config 目录下,这种约定使得开发者可以专注于业务逻辑,而不必关心文件的加载和管理。

elpis-core的主要功能

elpis-core主要是将磁盘中我们已经约定好的文件存储目录,在应用运行时,将资源文件解析加载在内存中,让我们能够以一种约定好的方式来读取和使用。

elpis-core的部分核心代码实现

  1. middleware-loader

    加载所有middlewares并挂在到应用的上下文中,使应用运行时可通过 app.middleware.xxx.xxx来访问中间件

module.exports = (app) => {
  //加载middlewares下面的所有文件
  //app.busniessDir为process.cwd()
  //获取app/middlewares下面的路径
  const middlewarePath = path.resolve(app.busniessDir, `.${sep}middleware`)
  //获取middlewares下面的所有文件名数组
  const fileList = glob.sync(path.resolve(middlewarePath, `.${sep}**${sep}**.js`)) //glob.sync return.resolve all arr of filenames
  //遍历middlewares下面的文件,将文件挂载到app.middleware上
  const middlewares = {}
  fileList.forEach(file => {
    //获取所有的文件名
    let fileName = path.resolve(file)
    //截取路径
    fileName = fileName.substring(fileName.lastIndexOf(`middleware${sep}`) + `middleware${sep}`.length, fileName.lastIndexOf('.'))
    //将名字改为驼峰式命名
    fileName = fileName.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase())
    //将middlewares挂在到app对象上
    let tempMiddleware = middlewares
    const fileNames = fileName.split(`${sep}`) //将文件名拆分成数组
    for (let i = 0, len = fileNames.length; i < len; i++) {
      if (i === len - 1) { //说明找到了对应的js文件
        tempMiddleware[fileNames[i]] = require(path.resolve(file))(app) 
      } else { //当前目录
        if (!tempMiddleware[fileNames[i]]) { 
          tempMiddleware[fileNames[i]] = {}  
        }
        tempMiddleware = tempMiddleware[fileNames[i]]  tempMiddleware[fileNames[i]]
      }
    }
  });
  app.middlewares = middlewares
}
  1. controller-loader

    加载所有的controller并挂在到应用的上下文中,使运行时可通过 app.controller.xxx.xxx访问相应控制器

module.exports = (app) => {
  //加载controllers下面的所有文件
  //获取app/controllers下面的路径
  const controllerPath = path.resolve(app.busniessDir, `.${sep}controller`)
  //获取controllers下面的所有文件名数组
  const fileList = glob.sync(path.resolve(controllerPath, `.${sep}**${sep}**.js`)) //glob.sync return.resolve all arr of filenames
  //遍历controllers下面的文件,将文件挂载到app.controller上
  const controllers = {}
  fileList.forEach(file => {
    //获取所有的文件名
    let fileName = path.resolve(file)
    //截取路径
    fileName = fileName.substring(fileName.lastIndexOf(`controller${sep}`) + `controller${sep}`.length, fileName.lastIndexOf('.'))
    //将名字改为驼峰式命名
    fileName = fileName.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase())
    //将controllers挂在到app.controller上对象上
    let tempController = controllers
    const fileNames = fileName.split(`${sep}`) //将文件名拆分成数组
    for (let i = 0, len = fileNames.length; i < len; i++) {
      if (i === len - 1) { //说明找到了对应的js文件
        const ControllerModule = require(path.resolve(file))(app)
        tempController[fileNames[i]] = new ControllerModule() //每一个controller都是一个对象
      } else { //当前目录
        if (!tempController[fileNames[i]]) { 
          tempController[fileNames[i]] = {} 
        }
        tempController = tempController[fileNames[i]]     
      }
    }
  });
  app.controller = controllers
}
  1. router-loader

    读取app/router下面的文件将其注册到Koa-router上

module.exports = (app) => {
  //读取文件路径
  const routerPath = path.resolve(app.busniessDir, `.${sep}router`)
  //实例化KoaRouter
  const router = new KoaRouter()
  //注册所有router到KoaRouter上
  const fileList = glob.sync(path.resolve(routerPath, `.${sep}**${sep}**.js`))
  fileList.forEach(file => {
    require(path.resolve(file))(app, router)
  })
  //兜底路由(健壮性考虑)
  router.get('*', async (ctx, next) => {
    ctx.status = 302 //临时重定向
    ctx.redirect(`${app?.options?.homePath ?? '/'}`)
  })
  //将KoaRouter挂在到app.router上
  app.use(router.routes())
  app.use(router.allowedMethods())
}

总结

elpis-core 通过模块化的设计和约定优于配置的理念,提供了一种高效、灵活的文件加载和管理机制。其主要优势包括:

  • 简化开发流程:通过自动加载和管理文件,开发者可以专注于业务逻辑。

  • 提高可维护性:模块化的设计使得代码更易于维护和扩展。

  • 增强灵活性:支持多环境配置和动态加载,使得应用可以根据不同的运行环境和需求进行调整。

  • 减少重复代码:通过统一的加载和访问机制,减少了手动加载和管理文件的重复代码。

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