前言
elpis是一个企业级应用框架,旨在能够解决日常开发中80%的重复CRUD操作,以及20%的定制开发需求,提高开发效率,本文将简单介绍elpis中的服务端解析引擎——eplis-core
elpis-core的架构设计
模块化设计
- elpis-core将应用中各个功能模块(中间件、环境配置、控制器等)分离成不同的loader来解析和加载,每个loader负责特定的模块解析和加载,确保模块的职责清晰。
约定优于配置
- 通过约定的目录结构和文件命名,减少配置的复杂性。例如,控制器文件放在 app/controller 目录下,配置文件放在 config 目录下,这种约定使得开发者可以专注于业务逻辑,而不必关心文件的加载和管理。
elpis-core的主要功能
elpis-core主要是将磁盘中我们已经约定好的文件存储目录,在应用运行时,将资源文件解析加载在内存中,让我们能够以一种约定好的方式来读取和使用。
elpis-core的部分核心代码实现
-
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
}
-
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
}
-
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 通过模块化的设计和约定优于配置的理念,提供了一种高效、灵活的文件加载和管理机制。其主要优势包括:
-
简化开发流程:通过自动加载和管理文件,开发者可以专注于业务逻辑。
-
提高可维护性:模块化的设计使得代码更易于维护和扩展。
-
增强灵活性:支持多环境配置和动态加载,使得应用可以根据不同的运行环境和需求进行调整。
-
减少重复代码:通过统一的加载和访问机制,减少了手动加载和管理文件的重复代码。
注:引用抖音“哲玄前端”,《全栈实践课》