1️⃣ Egg.js 的设计哲学
核心概念
- 约定优于配置(Convention over Configuration) :你只要遵循 Egg.js 规定的目录和命名规则,就能减少配置和重复工作。
- 多进程模型(Cluster 模型) :Egg.js 天生支持多进程,让应用可以高并发稳定运行。
类比
-
想象你在一家餐厅:
- 约定 = 菜单的固定格式和操作流程
- 优于配置 = 你不用每次都写完整流程,只要按菜单走就行
-
多进程模型就像:
- 有一位总厨(Master)和多个厨师(Worker)同时做菜,保证高峰期客户不等太久。
核心理解
- 遵循 Egg.js 的约定,你可以快速开发,而不用重复写模板代码。
- Cluster 模型保证稳定性和高并发。
2️⃣ 目录结构与环境
核心概念
-
目录结构是 Egg.js 的灵魂,每个目录都有固定作用:
app/controller→ 控制器,处理请求app/service→ 服务层,处理业务逻辑app/router.js→ 路由,决定 URL 对应哪个 Controllerconfig→ 配置文件,不同环境可区分
-
环境变量 (
EGG_SERVER_ENV) 决定使用哪个配置文件(local、prod、test)。
类比
-
想象公司大楼:
router.js= 前台接待controller= 服务员service= 厨师config= 菜单规范和厨房规则
核心理解
- 熟悉目录结构,才能快速找到业务逻辑和修改入口。
- 不同环境配置保证开发和生产环境分离,安全又稳定。
3️⃣ Router(路由)
核心概念
- Router 决定 前端请求哪个 URL 调用哪个 Controller
- 支持多种请求方法:
GET,POST,PUT,DELETE
类比
-
Router 就像餐厅前台:
- 客户说 “我要一份炸鸡”,前台决定把请求送到炸鸡窗口的厨师。
核心理解
- Router 只是“入口指引”,不做业务逻辑。
- 绑定方法和 URL 是最基本任务。
4️⃣ Controller(控制器)
核心概念
- Controller 处理路由传来的请求,调用 Service 获取业务逻辑结果,然后返回给客户端。
类比
-
Controller = 服务员
- 前台把订单交给服务员
- 服务员向厨师下单
- 最后把菜端给顾客
核心理解
- Controller 负责“请求进来 → 调用逻辑 → 响应出去”,不做具体业务处理。
- Controller 是接口测试的核心对象。
5️⃣ Service(业务逻辑)
核心概念
- Service 专门处理业务逻辑,可以被多个 Controller 调用。
- 可以访问数据库、调用 Helper、封装复杂逻辑。
类比
-
Service = 厨师
- 接到服务员订单后做菜
- 不管前台和客户,只管菜做好
核心理解
- Service 的测试重点是“逻辑正确性”,可以独立于 HTTP 测试。
- Controller 和 Router 调用它,实现接口功能。
6️⃣ Context(上下文对象,ctx)
核心概念
-
ctx是每次请求的上下文- 包含请求信息、响应方法、Service、Helper、日志等
类比
-
ctx = 当天服务员手里拿的“订单本”
- 上面有客户信息、菜品、餐桌号
- 服务员可以直接调用厨师做菜(Service)
- 写日志、记录点餐时间
核心理解
- ctx 是 贯穿整个请求生命周期的核心对象
- 方便 Controller 和 Service 获取请求数据、操作响应、调用日志
7️⃣ Config(配置管理)
核心概念
-
Egg.js 配置统一管理,可以分环境:
config.default.js→ 默认配置config.local.js→ 本地开发config.prod.js→ 生产
-
配置可以控制安全、日志、数据库、端口等
类比
-
config = 餐厅规则
- 菜谱、厨房温度、订单流程
- 开发环境可以宽松,生产环境严格安全
核心理解
- 配置中心化,易于维护
- 不同环境切换简单
8️⃣ Middleware(中间件)
核心概念
- 中间件是请求和响应之间的处理程序
- 可以做日志、鉴权、数据校验、性能监控
类比
-
Middleware = 门口安检 + 服务员检查
- 检查顾客身份(鉴权)
- 检查点餐格式(校验)
- 记录点餐时间(日志)
核心理解
- 每个请求都可以经过中间件链
- 可以在全局或者局部注册
9️⃣ Extend(扩展工具)
核心概念
- 可以扩展 Controller、Service、Context、Application 的方法
- 统一封装常用工具或逻辑
类比
-
Extend = 厨房工具升级
- 比如:统一封装炒菜机器,让厨师不用每次手工切菜
核心理解
- 提高开发效率
- 保持代码 DRY(不要重复自己)
🔟 Schedule(定时任务)
核心概念
- 定时执行的任务(cron job)
- 比如清理日志、发送邮件、数据统计
类比
-
Schedule = 厨房自动烤箱
- 到指定时间自动启动做菜
- 不需要服务员触发
核心理解
- 与 Controller 无关
- 可保证业务定期执行
1️⃣1️⃣ Logger(日常日志)
核心概念
- Egg.js 提供 Logger 系统
- 记录 info、warn、error 日志
- 支持文件分级写入
类比
-
Logger = 厨师的工作记录本
- 每次做菜、发生问题都记录
- 方便以后查错、统计
核心理解
- 不要用 console.log
- Logger 结构化、可查询、适合运维
1️⃣2️⃣ 单元测试与集成测试
核心概念
- Controller 测试 → 测接口行为(是否按预期返回)
- Service 测试 → 测业务逻辑(不走 HTTP)
egg-mock提供app.mockContext()创建假 ctx
类比
- Controller 测试 = 顾客点菜 → 最终上菜是否正确
- Service 测试 = 厨师做菜是否正确
- Mock ctx = 模拟服务员拿着订单本,不去餐厅也能测试厨师
核心理解
- 测试覆盖两层:接口 & 业务逻辑
- 让代码更稳定、易维护
✅ 总结口诀(方便新手快速复习)
路由指路 → 控制接单 → 厨师做菜 → 上下文拿单 → 配置定规则 → 中间件保安全 → 扩展工具提效率 → 定时任务自动跑 → 日志留痕 → 测试保稳定
换成人话就是:
- 前台(Router)告诉服务员(Controller)谁要点什么
- 服务员把订单传给厨师(Service)
- 厨师通过订单本(ctx)做菜
- 厨房工具(Extend)和规则(Config、中间件)保证顺畅
- 自动烤箱(Schedule)按时做任务
- 日志(Logger)记录每一步
- 测试保证菜和流程没问题
1️⃣ Router(路由)
文件位置
app/router.js
作用
- URL 请求 → 对应 Controller 方法
标准写法
module.exports = app => {
const { router, controller } = app;
// GET 请求
router.get('/ping', controller.home.ping);
// POST 请求
router.post('/user/create', controller.user.create);
};
类比
- Router = 前台接待,指路给服务员(Controller)
2️⃣ Controller(控制器)
文件位置
app/controller/home.js
作用
- 接收请求,调用 Service,返回响应
标准写法
'use strict';
const Controller = require('egg').Controller;
class HomeController extends Controller {
async ping() {
const { ctx } = this;
ctx.body = { message: 'pong' };
}
}
module.exports = HomeController;
类比
- Controller = 服务员,负责拿订单 → 厨师 → 返回给顾客
3️⃣ Service(业务逻辑)
文件位置
app/service/user.js
作用
- 处理核心业务逻辑,可被多个 Controller 调用
标准写法
'use strict';
const Service = require('egg').Service;
class UserService extends Service {
async create(user) {
if (!user.username) throw new Error('username required');
// 模拟数据库返回
return { id: 1, username: user.username };
}
}
module.exports = UserService;
类比
- Service = 厨师,只管做菜,不管客户
4️⃣ Context(上下文对象 ctx)
使用位置
- 在 Controller 或 Service 里
示例
class HomeController extends Controller {
async info() {
const { ctx } = this;
// 获取请求参数
const name = ctx.query.name || 'guest';
// 调用 Service
const result = await ctx.service.user.create({ username: name });
ctx.body = result;
}
}
类比
- ctx = 服务员手里的“订单本”,记下客户信息和需求
5️⃣ Config(配置管理)
文件位置
config/config.default.js
config/config.local.js
config/config.prod.js
示例
exports.keys = 'your-cookie-sign-key';
exports.security = {
csrf: {
enable: true,
},
};
exports.logger = {
level: 'INFO',
};
exports.mysql = {
client: {
host: 'localhost',
port: '3306',
user: 'root',
password: '123456',
database: 'egg_demo',
},
app: true,
agent: false,
};
类比
- config = 餐厅规则和菜单,规定流程和资源
6️⃣ Middleware(中间件)
文件位置
app/middleware/logger.js
示例
module.exports = () => {
return async function logger(ctx, next) {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
};
};
注册中间件
// config/config.default.js
exports.middleware = ['logger'];
类比
- Middleware = 门口安检 + 服务员检查,保证流程和安全
7️⃣ Extend(扩展工具)
文件位置
app/extend/context.js
示例
module.exports = {
success(data = {}) {
this.body = { code: 0, data };
},
};
Controller 使用
async info() {
const { ctx } = this;
ctx.success({ message: 'ok' });
}
类比
- Extend = 厨房工具升级,封装常用操作,提高效率
8️⃣ Schedule(定时任务)
文件位置
app/schedule/clear-log.js
示例
'use strict';
const Subscription = require('egg').Subscription;
class ClearLog extends Subscription {
static get schedule() {
return {
cron: '0 0 * * *', // 每天0点执行
type: 'all', // 所有 worker 都执行
};
}
async subscribe() {
const { app } = this;
app.logger.info('开始清理日志...');
// 执行清理逻辑
}
}
module.exports = ClearLog;
类比
- Schedule = 自动烤箱,按时自动运行任务
9️⃣ Logger(日志)
使用位置
- Controller / Service / Schedule / Middleware
示例
// info
ctx.logger.info('用户创建成功', userId);
// error
ctx.logger.error(new Error('数据库错误'));
类比
- Logger = 厨师工作日志,记录每一步方便排查
🔟 单元测试
Controller 测试位置
test/controller/home.test.js
示例
const { app, assert } = require('egg-mock/bootstrap');
describe('Controller: home', () => {
it('GET /ping should return pong', async () => {
const result = await app.httpRequest().get('/ping');
assert(result.body.message === 'pong');
});
});
Service 测试位置
test/service/user.test.js
示例
const { app, assert } = require('egg-mock/bootstrap');
describe('Service: user', () => {
let ctx;
beforeEach(() => { ctx = app.mockContext(); });
it('should create user', async () => {
const result = await ctx.service.user.create({ username: 'tom' });
assert(result.username === 'tom');
});
});
类比
- Controller 测试 = 顾客点菜 → 上菜是否正确
- Service 测试 = 厨师做菜是否正确
复习口诀(配合代码记忆)
Router 指路 → Controller 接单 → Service 做菜 → ctx 拿单 → Config 定规则 → Middleware 保安全 → Extend 提效率 → Schedule 自动跑 → Logger 留痕 → 测试保稳定