最近实习时接手了一个Egg.js + TypeScript的服务端的项目,mentor对我说可以让我先学习一下egg.js,于是最近都在捣鼓egg.js,有了一点小收获于是准备先记录下来,避免自己之后又忘记了。
1. Egg.js 简介
Egg.js是什么?它的官方文档是这样说的:
Egg.js 为企业级框架和应用而生。我们希望 Egg.js 能孕育出更多上层框架,帮助开发团队和开发人员降低开发和维护成本。
众所周知,node是具备后端开发的能力的,而egg.js就是为了方便后端开发而诞生的一种框架,就和我们之前介绍过的koa一样,而恰好,Egg又是继承于koa的,都是基于中间件开发,都是基于洋葱模型。
Egg 奉行"约定优于配置 "。按照一套统一的约定开发应用时,团队成员可以减少学习成本,不再是"钉子",能流动起来。未有约定的团队,沟通成本极高,因为每个人的开发习惯可能都不同,容易犯错。但约定并不意味着扩展性差,Egg 的扩展性极高,可根据不同团队的约定来定制框架。使用 Loader,框架能够根据不同环境定义默认配置,并覆盖 Egg 的默认约定。
这是官方文档对于它的描述。好的,废话不多说,我们来看看它的一些简单的语法吧。
2. Egg.js 目录结构介绍
我们可以使用这条命令npm init egg --type=simple来快速初始化一个egg.js项目,一般egg.js的目录结构会有这几项内容:

其中app目录与config目录是最重要的两个目录,我们着重介绍一下这两个:
我们说过,egg.js奉行的是"约定优于配置"的开发方式,所以每个文件夹是做什么的都是约定好的:
js
app
├── controller # 控制器(处理请求)
├── service # 服务层(业务逻辑)
├── model # 数据库模型(可选)
├── middleware # 中间件
├── router.js # 路由配置
config
├── config.default.js # 默认配置
├── plugin.js # 插件配置
在egg.js中,还有一个非常重要的东西叫Context(上下文对象),各个文件夹之间的联通主要来靠它。每次请求会创建一个 Context 对象,它封装了Node.js 的 request 和 response 对象,并提供了一系列便捷方法。
我们可以在 Controller、Middleware、Service、Helper 中通过 this.ctx
访问。
接下来,我们将一一介绍一下app下的各个文件的作用是什么。
3. app/router.js 文件
作用:定义 URL 路由规则,将请求映射到 Controller。
示例配置:
js
module.exports = app => {
const { router, controller } = app;
router.post('/user/create', controller.user.create);
router.get('/user/list', controller.user.find);
};
当我们通过http请求访问/user/create
或/user/list
,就会调用对应的UserController.create
方法。
4. app/controller 目录
作用:处理 HTTP 请求,负责路由与业务逻辑之间的桥梁。
示例文件:app/controller/user.js
js
const Controller = require('egg').Controller;
class UserController extends Controller {
async create() {
const { ctx } = this;
const { name, age } = ctx.request.body;
// 调用 Service 处理业务逻辑
const user = await ctx.service.user.createUser(name, age);
ctx.body = { success: true, data: user };
}
async find() {
const users = await this.ctx.service.user.getUsers();
this.ctx.body = users;
}
}
module.exports = UserController;
用途:定义 /user/create
和 /user/find
路由对应的逻辑,联通service
业务层。
5. app/service 目录
作用:封装业务逻辑(如数据库操作),供 Controller 调用。
示例文件:app/service/user.js
js
const Service = require('egg').Service;
class UserService extends Service {
async createUser(name, age) {
// 假设使用 Egg.js 的 MySQL 插件
const result = await this.app.mysql.insert('users', { name, age });
return { id: result.insertId, name, age };
}
async getUsers() {
return await this.app.mysql.select('users');
}
}
module.exports = UserService;
用途:处理用户创建和查询的数据库操作。
6. app/middleware 目录
作用:存放中间件,用于拦截和处理请求。
示例文件:app/middleware/logger.js
js
module.exports = (options, app) => {
return async function logger(ctx, next) {
const start = Date.now();
await next(); // 执行后续中间件和路由
const duration = Date.now() - start;
console.log(`[${ctx.method}] ${ctx.url} - ${duration}ms`);
};
};
配置启用:在 config/config.default.js
中:
js
exports.middleware = ['logger']; // 启用中间件
7. app/view 目录
作用:存放模板文件(如 HTML 模板)。
示例文件:app/view/user/list.tpl
html
<!DOCTYPE html>
<html>
<head>
<title>User List</title>
</head>
<body>
<h1>Users</h1>
<ul>
{% for user in users %}
<li>{{ user.name }} ({{ user.age }})</li>
{% endfor %}
</ul>
</body>
</html>
在 Controller 中渲染:
js
async list() {
const users = await this.ctx.service.user.getUsers();
await this.ctx.render('user/list.tpl', { users });
}
8. app/extend 目录
作用:扩展 Egg.js 的核心对象(如 Application
、Context
等)。
示例文件:app/extend/context.js
js
module.exports = {
formatUser(user) {
return `User: ${user.name} (ID: ${user.id})`;
}
};
使用扩展方法:
js
// 在 Controller 中
ctx.formatUser(user); // 输出 "User: Alice (ID: 1)"
9. config 目录
作用:存放项目配置,区分不同环境(如开发、测试、生产)。
关键文件:
config.default.js(通用配置):
js
exports.mysql = {
client: {
host: 'localhost',
port: '3306',
user: 'root',
password: '123456',
database: 'test',
},
};
config.prod.js (生产环境覆盖配置)
js
exports.mysql = {
client: {
host: 'prod.db.com', // 生产环境数据库地址
},
};
10. 其他目录
-
app/public
:存放静态文件(如图片、CSS)。 -
app/schedule
:定时任务配置。 -
app/utils
(非官方约定):存放工具类函数。
11. 总结
简单总结来说的话,就是用户访问 /user/list
,router.js
将请求路由到 UserController.list
,Controller 调用 UserService.getUsers()
,Service 通过 app.mysql
查询数据库,Controller 使用 ctx.render
渲染模板,返回 HTML 页面或 JSON 数据。
egg.js的内容远不止这些,本次只是对于egg.js的目录的一些简单的介绍,如果对你有帮助的话请点个赞吧。