在 Node.js 蓬勃发展的当下,构建高效、可维护的企业级应用成为开发者的重要需求。Egg.js 作为阿里巴巴开源的一款基于 Koa 构建的企业级 Node.js 框架,以其卓越的设计理念和丰富的功能特性,为开发者提供了一套成熟的解决方案。它通过约定优于配置的原则,极大地提升了开发效率,同时保证了项目的可维护性和扩展性。接下来,我们将深入探究 Egg.js 的各个方面,从安装配置到具体的开发使用,全面掌握这一强大的框架。
Egg.js 核心概念
约定优于配置
Egg.js 遵循约定优于配置的原则,为项目制定了一套统一的目录结构和命名规范。这意味着开发者无需花费大量时间去配置各种复杂的选项,只需要按照约定的目录结构和命名方式组织代码,框架就能自动识别并处理。例如,控制器文件通常放在 app/controller
目录下,路由配置文件放在 app/router.js
中。这种约定使得团队协作更加高效,新成员也能快速上手项目。
插件机制
Egg.js 拥有强大的插件机制,通过插件可以方便地扩展框架的功能。插件是 Egg.js 生态系统的重要组成部分,开发者可以根据项目需求选择合适的插件,如数据库连接插件、缓存插件、日志插件等。插件的使用方式非常简单,只需要在配置文件中启用相应的插件即可。例如,使用 egg-mysql
插件可以轻松实现与 MySQL 数据库的连接和操作。
多进程模型
Egg.js 采用多进程模型,利用 Node.js 的 cluster
模块实现多进程的启动和管理。通过启动多个工作进程,可以充分利用多核 CPU 的性能,提高应用的并发处理能力。主进程负责监控和管理工作进程,当某个工作进程出现异常时,主进程会自动重启该进程,保证应用的稳定性。
Egg.js 的安装与配置
安装
要开始使用 Egg.js,首先需要安装 Node.js 和 npm(Node Package Manager)。确保你的 Node.js 版本在 14.x 及以上,npm 版本在 6.x 及以上。安装完成后,使用 npm 全局安装 Egg.js 的脚手架工具:
npm i egg-init -g
安装完成后,就可以使用 egg-init
命令来创建一个新的 Egg.js 项目。选择一个合适的目录,执行以下命令:
egg-init egg-example --type=simple
cd egg-example
npm i
上述命令创建了一个名为 egg-example
的简单 Egg.js 项目,并安装了项目的依赖。
配置
Egg.js 的配置文件位于 config
目录下,主要包括 config.default.js
、config.prod.js
、config.local.js
等。config.default.js
是默认的配置文件,包含了项目的通用配置;config.prod.js
是生产环境的配置文件,会覆盖 config.default.js
中相同的配置项;config.local.js
是本地开发环境的配置文件,同样会覆盖默认配置。
例如,要配置应用的端口号,可以在 config.default.js
中添加以下代码:
exports.cluster = {
listen: {
port: 7001
}
};
这样,应用启动时就会监听 7001 端口。
另外,还可以在配置文件中配置插件的使用。以 egg-mysql
插件为例,在 config.default.js
中添加以下配置:
exports.mysql = {
client: {
host: '127.0.0.1',
port: '3306',
user: 'root',
password: 'your_password',
database: 'your_database'
},
app: true,
agent: false
};
然后在 config/plugin.js
中启用该插件:
exports.mysql = {
enable: true,
package: 'egg-mysql'
};
Egg.js 的项目结构
主要目录和文件
- app:应用核心代码目录,包含控制器、服务、模型等文件。
- controller:存放控制器文件,负责处理业务逻辑和返回响应。
- service:存放服务文件,封装业务逻辑,供控制器调用。
- model:存放模型文件,用于与数据库交互。
- router.js:路由配置文件,定义请求的路由规则。
- config:配置文件目录,包含不同环境的配置文件。
- test:测试文件目录,用于编写单元测试和集成测试。
- app.js:应用启动入口文件,可以在这里进行一些初始化操作。
- agent.js:Agent 进程启动入口文件,用于处理一些定时任务或全局初始化操作。
各部分的作用
- 控制器(Controller):控制器是 Egg.js 中处理请求的核心部分。它接收客户端的请求,调用相应的服务处理业务逻辑,最后返回响应给客户端。例如,一个简单的控制器文件
app/controller/home.js
可以这样编写:
const Controller = require('egg').Controller;
class HomeController extends Controller {
async index() {
const { ctx } = this;
ctx.body = 'Hello, Egg.js!';
}
}
module.exports = HomeController;
- 服务(Service):服务用于封装业务逻辑,将复杂的业务逻辑从控制器中分离出来,提高代码的可维护性和复用性。例如,创建一个
app/service/user.js
文件:
const Service = require('egg').Service;
class UserService extends Service {
async getUserById(id) {
// 模拟从数据库中获取用户信息
return { id, name: 'John Doe' };
}
}
module.exports = UserService;
然后在控制器中调用该服务:
const Controller = require('egg').Controller;
class UserController extends Controller {
async getUser() {
const { ctx } = this;
const id = ctx.query.id;
const user = await ctx.service.user.getUserById(id);
ctx.body = user;
}
}
module.exports = UserController;
- 模型(Model):模型主要用于与数据库进行交互。如果使用
egg-mysql
插件,可以在模型中编写 SQL 语句进行数据库操作。例如,创建一个app/model/user.js
文件:
module.exports = app => {
const { STRING, INTEGER } = app.Sequelize;
const User = app.model.define('user', {
id: { type: INTEGER, primaryKey: true, autoIncrement: true },
name: STRING(30)
});
return User;
};
在服务中使用模型进行数据库操作:
const Service = require('egg').Service;
class UserService extends Service {
async getUserById(id) {
const user = await this.ctx.model.User.findByPk(id);
return user;
}
}
module.exports = UserService;
Egg.js 的路由与控制器
路由配置
路由配置文件 app/router.js
用于定义请求的路由规则。在这个文件中,可以使用 app.get
、app.post
、app.put
、app.delete
等方法来定义不同类型的请求路由。例如:
module.exports = app => {
const { router, controller } = app;
router.get('/', controller.home.index);
router.get('/user/:id', controller.user.getUser);
router.post('/user', controller.user.createUser);
};
上述代码定义了三个路由规则:
- 根路径
/
的 GET 请求由home
控制器的index
方法处理。 /user/:id
的 GET 请求由user
控制器的getUser
方法处理,其中:id
是路由参数。/user
的 POST 请求由user
控制器的createUser
方法处理。
控制器处理请求
控制器负责处理路由匹配后的请求。在控制器中,可以通过 ctx
对象获取请求信息,如请求参数、请求头、请求体等,并返回响应。例如,user
控制器的 getUser
方法可以这样实现:
const Controller = require('egg').Controller;
class UserController extends Controller {
async getUser() {
const { ctx } = this;
const id = ctx.params.id;
const user = await ctx.service.user.getUserById(id);
if (user) {
ctx.body = user;
} else {
ctx.status = 404;
ctx.body = { message: 'User not found' };
}
}
}
module.exports = UserController;
在这个方法中,首先从 ctx.params
中获取路由参数 id
,然后调用服务的 getUserById
方法获取用户信息。如果用户存在,返回用户信息;否则,返回 404 状态码和错误信息。
Egg.js 的服务与中间件
服务的使用
服务是 Egg.js 中封装业务逻辑的重要组件。服务可以被多个控制器复用,提高代码的可维护性。在服务中,可以调用其他服务、模型或进行一些复杂的计算。例如,在 user
服务中添加一个更新用户信息的方法:
const Service = require('egg').Service;
class UserService extends Service {
async updateUser(id, data) {
const user = await this.ctx.model.User.findByPk(id);
if (user) {
await user.update(data);
return user;
}
return null;
}
}
module.exports = UserService;
在控制器中调用该服务:
const Controller = require('egg').Controller;
class UserController extends Controller {
async updateUser() {
const { ctx } = this;
const id = ctx.params.id;
const data = ctx.request.body;
const user = await ctx.service.user.updateUser(id, data);
if (user) {
ctx.body = user;
} else {
ctx.status = 404;
ctx.body = { message: 'User not found' };
}
}
}
module.exports = UserController;
中间件的使用
中间件是 Egg.js 中处理请求的中间环节,可以在请求到达控制器之前或响应返回客户端之前进行一些预处理或后处理操作。中间件可以用于日志记录、权限验证、请求参数验证等。创建一个中间件文件 app/middleware/logger.js
:
module.exports = () => {
return async function logger(ctx, next) {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.logger.info(`Request to ${ctx.url} took ${ms}ms`);
};
};
在 config.default.js
中配置中间件:
exports.middleware = ['logger'];
这样,每次请求都会记录请求的处理时间。
总结
Egg.js 作为一款优秀的企业级 Node.js 框架,凭借其约定优于配置的原则、强大的插件机制和多进程模型,为开发者提供了高效、可维护的开发体验。通过合理的安装配置,掌握其项目结构、路由与控制器、服务与中间件的使用方法,开发者能够快速搭建出高质量的 Node.js 应用。无论是处理复杂的业务逻辑,还是进行高并发的请求处理,Egg.js 都能展现出其卓越的性能和稳定性。