Egg.js
搭建工程
Egg.js 为企业级框架和应用而生,我们希望由 Egg.js 孕育出更多上层框架,帮助开发团队和开发人员降低开发和维护成本。
使用脚手架搭建应用程序 快速初始化项目
npm init egg --type=simple
npm i
此时目录结构如下
在app>controller>??.js中书写控制层
在app>router.js中书写路由匹配规则
egg-example
├── app
│ ├── controller
│ │ └── home.js
│ └── router.js
├── config
│ └── config.default.js
└── package.json
home.js代码如下
// app/controller/home.js
const Controller = require('egg').Controller;
class HomeController extends Controller {
async index() {
this.ctx.body = 'Hello world';
}
}
module.exports = HomeController;
router.js代码如下
// app/router.js
module.exports = (app) => {
const { router, controller } = app;
router.get('/', controller.home.index);
};
路由和控制器
路由(Router)
Router 主要用来描述请求 URL 和具体承担执行动作的 Controller 的对应关系, 框架约定了 app/router.js 文件用于统一所有路由规则。
内部重定向
// app/router.js
module.exports = (app) => {
app.router.get('index', '/home/index', app.controller.home.index);
app.router.redirect('/', '/home/index', 302);
};
太多路由映射
如上所述,我们并不建议把路由规则逻辑散落在多个地方,会给排查问题带来困扰。
'use strict';
const routers = {
home(router, controller) {
return router.get('/home', controller.home.index);
},
}
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
const { router, controller } = app;
// 路由配置
Object.values(routers).forEach((item) => {
item(router, controller)
})
};
RESTful风格配置
如果想通过 RESTful 的方式来定义路由, 我们提供了 app.router.resources('routerName', 'pathMatch', controller) 快速在一个路径上生成 CRUD 路由结构。
// app/router.js
module.exports = (app) => {
const { router, controller } = app;
router.resources('posts', '/api/posts', controller.posts);
router.resources('users', '/api/v1/users', controller.v1.users); // app/controller/v1/users.js
};
上面代码就在 /posts 路径上部署了一组 CRUD 路径结构,对应的 Controller 为 app/controller/posts.js 接下来, 你只需要在 posts.js 里面实现对应的函数就可以了。
// app/controller/posts.js
const { Controller } = require('egg');
class Posts extends Controller {
index() {
this.ctx.body = '获取列表';
};
new() {
this.ctx.body = '新增表单';
};
show() {
this.ctx.body = '博客详情';
};
edit() {
this.ctx.body = '编辑页面';
};
create() {
this.ctx.body = '新增博客';
};
update() {
this.ctx.body = '修改博客';
};
destroy() {
this.ctx.body = '删除博客';
};
}
module.exports = Posts;
静态资源
内部使用egg-static插件完成
app > public 目录下的所有文件
访问 http://localhost:7002/public/index.html
可以获取app/public/index.html文件信息
插件
egg的插件命名规范为 egg-*
比如static插件名称为egg-static
插件的启用
安装完成后,并没有启用插件,需要在config/plugin.js中启用插件。
内置插件是会自动启动。
/** @type Egg 的插件 */
module.exports = {
// 插件名称
static: {
enable: true,//是否启用
package: "egg-static",//插件包名
// path:"",//自定义插件 跟package只能出现一个
},
};
插件配置
插件一般会包含自己的默认配置,应用开发者可以在 config.default.js 覆盖对应的配置:
// config/config.default.js
module.exports = appInfo => {
const config = exports = {};
// cookie签名
config.keys = appInfo.name + '_1678169356863_9169';
// 添加用户配置
const userConfig = {
// myAppName: 'egg',
};
// 配置static插件
exports.static = {
prefix: '/',
};
return {
...config,
...userConfig,
};
};
egg-view-ejs 模板引擎
// {app_root}/config/plugin.js
exports.ejs = {
enable: true,
package: 'egg-view-ejs',
};
// {app_root}/config/config.default.js
exports.view = {
mapping: {
'.ejs': 'ejs',
},
};
中间件
Egg 的中间件形式和 Koa 的中间件形式是一样的,都是基于洋葱圈模型。每次我们编写一个中间件,就相当于在洋葱外面包了一层。
中间件
koa中间件
// app/middleware/bodyparser.js
// koa-compress 暴露的接口(`(options) => middleware`)和框架对中间件要求一致
module.exports = require('koa-bodyparser');
//app/config/config.default.js
// 配置需要的中间件,数组顺序即为中间件的加载顺序
config.middleware = ['bodyparser'];
全局中间件
// app/middleware/myMiddleware.js
//一个中间件
module.exports = (options) => {
return async function myMiddleware(ctx, next) {
await next();
};
};
//配置config.default.js
module.exports = {
// 配置需要的中间件,数组顺序即为中间件的加载顺序
middleware: ['myMiddleware'],
// 配置 myMiddleware中间件的配置
myMiddleware: {
enable:boolean,//是否启用中间件
match:"/login",//匹配为true才运行中间件
ignore:"/api",//忽略那些路径
}
};
路由中间件
正对某个路由控制器使用中间件
app/router.js
resources(router, controller, app) {
const myMiddleware = app.middleware.myMiddleware({ a: 1024 });
router.resources('blogs', '/api/blogs', myMiddleware, controller.blogs);
}
服务(Service)
简单来说,Service 就是在复杂业务场景下用于做业务逻辑封装的一个抽象层,提供这个抽象有以下几个好处:
保持 Controller 中的逻辑更加简洁。
保持业务逻辑的独立性,抽象出来的 Service 可以被多个 Controller 重复调用。
服务
定义 Service
// app/service/user.js
const Service = require('egg').Service;
class UserService extends Service {
async find(uid) {
const user = await this.ctx.db.query(
'select * from user where uid = ?',
uid,
);
return user;
}
}
module.exports = UserService;
访问service
Service 文件必须放在 app/service 目录,可以支持多级目录,访问的时候可以通过目录名级联访问。
ctx.service.syncUser.func(params)
app/service/biz/user.js => ctx.service.biz.user
app/service/sync_user.js => ctx.service.syncUser
// app/controller/user.js 控制层访问
const Controller = require('egg').Controller;
class UserController extends Controller {
async info() {
const { ctx } = this;
const userId = ctx.params.id;
const userInfo = await ctx.service.syncUser.find(userId);
ctx.body = userInfo;
}
}
module.exports = UserController;
数据库
MySql数据库
egg-sequelize
在一些较为复杂的应用中,我们可能会需要一个 ORM 框架来帮助我们管理数据层的代码。而在 Node.js 社区中,sequelize 是一个广泛使用的 ORM 框架,它支持 MySQL、PostgreSQL、SQLite 和 MSSQL 等多个数据源。
Sequelize框架
安装
npm install --save egg-sequelize mysql2
在 config/plugin.js 中引入 egg-sequelize 插件 使用
exports.sequelize = {
enable: true,
package: 'egg-sequelize',
};
在 config/config.default.js 中编写 sequelize 配置
exports.sequelize = {
logging: false,
dialect: 'mysql',
host: '127.0.0.1',
port: 3306,
database: 'myegg',
username: "root",
password: "5201314025ma",
define: {
timestamps: false, // 关键配置,默认为 true, 修改为 false 即可
freezeTableName: false,
}
};
模型定义和模拟数据
模型定义,模型同步,模拟数据,建立表关系。
数据模拟
// app/model/user 模型
// const Mock = require("mockjs")
// const resultStu = Mock.mock({
// "datas|200-300": [
// {
// "id|+1": 1,
// name: "@cname",
// loginId: "@id",
// loginPwd: /\w{10}/,
// created_at: "@date",
// updated_at: "@date",
// "auth|1": ['user', 'admin'],
// }
// ]
// }).datas;
module.exports = (app) => {
const { STRING, INTEGER, DATE } = app.Sequelize;
const User = app.model.define('user', {
id: { type: INTEGER, primaryKey: true, autoIncrement: true },
name: STRING(20),
loginId: { type: STRING(20), allowNull: false },
loginPwd: { type: STRING, allowNull: false },
created_at: DATE,
updated_at: DATE,
auth: STRING(20),
});
User.associate = function () {
app.model.sync({ alter: true }).then(() => {
console.log(19, '所有同步完成');
})
app.model.Class.hasMany(app.model.Student);
app.model.Student.belongsTo(app.model.Class);
console.log(22, '关联表');
// app.model.Student.bulkCreate(resultStu);
};
return User;
};
controller class
app/controller/class.js
//查询
const { Controller } = require('egg');
class Class extends Controller {
async index() {
const ctx = this.ctx;
const query = ctx.query
//调用service
ctx.body = await ctx.service.class.find(query);
}
}
module.exports = Class;
service class
app/service/class
const Service = require('egg').Service;
class UserService extends Service {
async find(query) {
const { page, limit } = query;
const result = await this.ctx.model.Class.findAndCountAll({
offset: (+page - 1) * +limit,
limit: +limit,
include: [this.ctx.model.Student],
})
return (result);
}
}
module.exports = UserService;
egg-redis
安装
npm i egg-redis --save
//config/plugin.js
exports.redis = {
enable: true,
package: 'egg-redis',
}
//config.default.js
config.redis = {
client: {
port: 6379, // Redis port
host: '127.0.0.1', // Redis host
password: 'auth', //必填
db: 0,
},
}
app/controller/user.js
const { Controller } = require('egg');
class Posts extends Controller {
async index() {
await this.app.redis.set('foo', 'bar11111111111');
// get
this.ctx.body = await this.app.redis.get('foo');
}
}