Egg.js 简介
- Egg.js 是一个基于 Koa 的 Node.js 服务端框架。
- Egg.js 通过框架约定进行开发,提高开发效率和规范性。
- Egg.js 通过框架插件的方式,提供了更多的功能,比如:安全、Session、定时任务、邮件、队列、MySQL、Redis、Elasticsearch、GraphQL、Vue、React、Webpack 等等。
Egg.js 特性
- 提供基于 Egg 定制上层框架的能力
- 高度可扩展的插件机制
- 内置多进程管理
- 基于 Koa 开发,性能优异
- 框架稳定,测试覆盖率高
- 渐进式开发
Egg.js 适用场景
- 服务端渲染
- 大中型网站后台
- 微服务
Egg.js 目录结构
egg-project
├── package.json
├── app.js (可选) (入口文件)
├── agent.js (可选) (Agent 入口文件)
├── app
| ├── router.js
│ ├── controller
│ | └── home.js
│ ├── service
│ | └── user.js
│ ├── middleware
│ | └── response_time.js
│ ├── schedule (可选)
│ | └── my_task.js
│ ├── public (可选)
│ | └── reset.css
│ ├── view (可选)
│ | └── home.tpl
│ ├── extend (可选)
│ | ├── helper.js
│ | ├── request.js
│ | ├── response.js
│ | ├── context.js
│ | └── application.js
│ ├── plugin.js (可选)
│ └── config
│ ├── config.default.js
│ ├── config.prod.js
│ ├── config.test.js
│ ├── config.local.js (可选)
│ ├── plugin.js (可选)
│ └── config.override.js (可选)
├── test
| ├── middleware
│ | └── response_time.test.js
│ └── controller
│ └── home.test.js
└── logs (可选)
Egg.js 基础
安装
# 安装脚手架
npm i egg-init -g
# 或者直接
npm init egg --type=simple
# 创建项目
npm init egg --type=simple
#或者 ts 版本
npm init egg --type=ts
# 框架开发
npm init egg --type=framework
# 插件开发
npm init egg --type=plugin
启动
# 安装依赖
npm i
# 启动项目
npm run dev
# 启动单元测试
npm test
# 启动线上环境
npm start
# 停止线上环境
npm stop
# 查看日志
npm run log
配置
// config/config.default.js
module.exports = (appInfo) => {
const config = (exports = {})
// 开发时候设置为 false,线上环境设置为 true
config.security = {
csrf: {
enable: false,
},
}
// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_demo'
// add your middleware config here
config.middleware = []
// 配置需要的插件,数组顺序即为插件的加载顺序
// static 插件默认配置
config.static = {
prefix: '/',
dir: path.join(appInfo.baseDir, 'app/public'),
dynamic: true,
preload: false,
buffer: false,
maxFiles: 1000,
}
// 配置需要的插件 egg-mysql
// 配置 mysql 插件信息
config.mysql = {
client: {
// host
host: 'mysql.com',
// 端口号
port: '3306',
// 用户名
user: 'test_user',
// 密码
password: 'test_password',
// 数据库名
database: 'test',
// 是否加载到 app 上,默认开启
app: true,
// 是否加载到 agent 上,默认关闭
agent: false,
},
}
// 配置需要的插件 egg-redis
// 配置 redis 插件信息
config.redis = {
client: {
port: 6379, // Redis port
host: '127.0.0.1', // Redis host
password: '',
db: 0,
},
}
// 配置需要的插件 egg-sequelize
// 配置 sequelize 插件信息
config.sequelize = {
dialect: 'mysql', // support: mysql, mariadb, postgres, mssql
database: 'test',
host: 'mysql.com',
port: '3306',
username: 'test_user',
password: 'test_password',
define: {
timestamps: false, // 默认不加时间戳
freezeTableName: true, // 默认不修改表名
},
}
// 配置需要的插件 egg-view-nunjucks
// 配置模板引擎
config.view = {
defaultViewEngine: 'nunjucks',
mapping: {
'.tpl': 'nunjucks',
},
}
// 配置需要的插件 egg-graphql
// 配置 graphql 插件信息
config.graphql = {
router: '/graphql',
app: true,
agent: false,
graphiql: true,
apolloServerOptions: {
tracing: true,
},
}
// 配置需要的插件 egg-jwt
// 配置 jwt 插件信息
config.jwt = {
// 秘钥
secret: '123456',
// 过期时间
sign: {
expiresIn: '1d',
},
}
// 配置需要的插件 egg-validate
// 配置 validate 插件信息
config.validate = {
// convert: false,
// validateRoot: false,
}
// 配置需要的插件 egg-cors
// 配置 cors 插件信息
config.cors = {
origin: '*',
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH',
}
// add your user config here
const userConfig = {
// myAppName: 'egg',
redisExpire: 60 * 60 * 24, // redis过期时间
}
return {
...config,
...userConfig,
}
}
中间件
- 编写中间件
// app/middleware/response_time.js
module.exports = () => {
return async function responseTime(ctx, next) {
const startTime = Date.now()
await next()
// 上报请求时间
ctx.set('X-Response-Time', Date.now() - startTime)
}
}
- 配置中间件
// config/config.default.js
// 配置中间件
config.middleware = ['responseTime']
// 配置中间件的配置项
config.responseTime = {
header: 'X-Response-Time',
}
路由
- 编写路由
// app/router.js
module.exports = (app) => {
const { router, controller } = app
router.get('/', controller.home.index)
}
控制器
- 编写控制器
// app/controller/home.js
const Controller = require('egg').Controller
class HomeController extends Controller {
async index() {
const { ctx } = this
ctx.body = 'hi, egg'
}
}
module.exports = HomeController
服务
- 编写服务
// app/service/user.js
const Service = require('egg').Service
class UserService extends Service {
async find(uid) {
const user = await this.ctx.model.User.findOne({
where: {
id: uid,
},
})
return user
}
}
module.exports = UserService
模型(Sequelize)
- 定义模型
// app/model/user.js
module.exports = (app) => {
const { STRING, INTEGER, DATE } = app.Sequelize
const User = app.model.define('user', {
id: { type: INTEGER, primaryKey: true, autoIncrement: true },
name: STRING(30),
age: INTEGER,
created_at: DATE,
updated_at: DATE,
})
return User
}
- 使用模型
// app/controller/user.js
const Controller = require('egg').Controller
class UserController extends Controller {
async index() {
const { ctx } = this
const users = await ctx.model.User.findAll()
ctx.body = users
}
}
module.exports = UserController
插件
- 安装插件
npm i egg-redis --save
- 配置插件
// config/plugin.js
// 配置插件
exports.redis = {
enable: true,
package: 'egg-redis',
}
- 使用插件
// app/controller/user.js
class UserController extends Controller {
async index() {
const { ctx } = this
const users = await ctx.model.User.findAll()
// 使用插件
const redis = await app.redis.get('default')
await redis.set('foo', 'bar')
const foo = await redis.get('foo')
ctx.body = {
users,
foo,
}
}
}
扩展
- 编写扩展
// app/extend/context.js 扩展context
module.exports = {
get isIOS() {
const iosReg = /iphone|ipad|ipod/i
return iosReg.test(this.get('user-agent'))
},
}
// app/extend/application.js 扩展application
module.exports = {
get isIOS() {
const iosReg = /iphone|ipad|ipod/i
return iosReg.test(this.get('user-agent'))
},
}
// app/extend/request.js 扩展request
module.exports = {
get isIOS() {
const iosReg = /iphone|ipad|ipod/i
return iosReg.test(this.get('user-agent'))
},
}
// app/extend/response.js 扩展response
module.exports = {
get isIOS() {
const iosReg = /iphone|ipad|ipod/i
return iosReg.test(this.get('user-agent'))
},
}
// app/extend/helper.js 扩展helper
module.exports = {
get isIOS() {
const iosReg = /iphone|ipad|ipod/i
return iosReg.test(this.get('user-agent'))
},
}
- 使用扩展
// app/controller/user.js
class UserController extends Controller {
async index() {
const { ctx } = this
const users = await ctx.model.User.findAll()
// 使用扩展
const isIOS = ctx.isIOS
ctx.body = {
users,
isIOS,
}
}
}
// app/service/user.js
class UserService extends Service {
async find(uid) {
const user = await this.ctx.model.User.findOne({
where: {
id: uid,
},
})
// 使用扩展 扩展application
const isIOS = this.app.isIOS
return {
user,
isIOS,
}
}
}
// app/middleware/response_time.js
module.exports = () => {
return async function responseTime(ctx, next) {
const startTime = Date.now()
await next()
// 上报请求时间
ctx.set('X-Response-Time', Date.now() - startTime)
// 使用扩展 扩展request
const isIOS = ctx.request.isIOS
// 使用扩展 扩展response
const isIOS = ctx.response.isIOS
// 使用扩展 扩展helper
const isIOS = ctx.helper.isIOS
}
}
定时任务
- 编写定时任务
// app/schedule/update_cache.js
module.exports = {
schedule: {
interval: '1m', // 1 分钟间隔
type: 'all', // 指定所有的 worker 都需要执行
},
async task(ctx) {
// ctx 就是 app 上下文,可以调用 ctx 上的其他方法,或访问属性
ctx.app.cache = {}
},
}
- 配置定时任务
// config/config.default.js
exports.schedule = {
interval: '1m', // 1 分钟间隔
type: 'all', // 指定所有的 worker 都需要执行
}
Egg.js 进阶
Loader(加载器)
- 加载器
- 加载顺序
// app.js
module.exports = (app) => {
console.log('app.js')
app.once('server', (server) => {
console.log('server')
})
app.on('error', (err, ctx) => {
console.log('error')
})
app.on('request', (ctx) => {
console.log('request')
})
app.on('response', (ctx) => {
console.log('response')
})
}
// agent.js
module.exports = (agent) => {
console.log('agent.js')
agent.messenger.on('egg-ready', () => {
console.log('egg-ready')
})
agent.messenger.on('xxx_action', (data) => {
console.log('xxx_action')
})
}
插件开发
编写一个自定义插件
// lib/plugin/egg-xxx/middleware/xxx.js
module.exports = (options) => {
return async function xxx(ctx, next) {
// 中间件的配置项,框架会将 app.config[${middlewareName}] 传递进来
// console.log(options)
// console.log(ctx)
// console.log(next)
await next()
}
}
// lib/plugin/egg-xxx/app/package.json
{
"name": "egg-xxx",
"eggPlugin":{
"name": "xxx"
}
}
// app/config/plugin.js
exports.xxx = {
enable: true,
path: path.join(__dirname, '../lib/plugin/egg-xxx'),
}
// app/config/config.default.js
// 参数, options 接收
exports.xxx = {
aaa: 'aaa',
bbb: 'bbb',
}
// 或者在app.js 中应用
// app.js
app.config.coreMiddleware.push('xxx')