1.Egg.js 为企业级框架和应用而生
egg特性:
1.提供基于 Egg 定制上层框架的能力
2.高度可扩展的插件机制
3.内置多进程管理
4.基于 Koa 开发,性能优异
5.框架稳定,测试覆盖率高
6.渐进式开发
egg使用场景
BFF层(前后端之间的中间层)、全栈、SSR(服务端渲染)
koa 、express 、egg区别
1.Koa 的中间件和 Express 不同,Koa 选择了洋葱圈模型。所有的请求经过一个中间件的时候都会执行两次,对比 Express 形式的中间件,Koa 的模型可以非常方便的实现后置处理逻辑,对比 Koa 和 Express 的 Compress 中间件就可以明显的感受到
Koa 中间件模型的优势。
.2expree和koa没有插件整合
3.Koa 和 Express 的设计风格非常类似,底层也都是共用的同一套 HTTP 基础库
4.Context和 Express 只有 Request 和 Response 两个对象不同,Koa 增加了一个 Context 的对象,作为这次请求的上下文对象。
5.Koa 是一个非常优秀的框架,然而对于企业级应用来说,它还比较基础。
而 Egg 选择了 Koa 作为其基础框架、Egg 继承于 Koa。
目录结构
egg-project
├── package.json
├── app.js (可选)
├── agent.js (可选)
├── 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 (可选)
│ └── agent.js (可选)
├── config
| ├── plugin.js
| ├── config.default.js
│ ├── config.prod.js
| ├── config.test.js (可选)
| ├── config.local.js (可选)
| └── config.unittest.js (可选)
└── test
├── middleware
| └── response_time.test.js
└── controller
└── home.test.js
app/router.js 用于配置 URL 路由规则,具体参见 Router。
app/controller/** 用于解析用户的输入,处理后返回相应的结果,具体参见 Controller。
app/service/** 用于编写业务逻辑层,可选,建议使用,具体参见 Service。
app/middleware/** 用于编写中间件,可选,具体参见 Middleware。
app/public/** 用于放置静态资源,可选,具体参见内置插件 egg-static。
app/extend/** 用于框架的扩展,可选,具体参见框架扩展。
config/config.{env}.js 用于编写配置文件,具体参见配置。
config/plugin.js 用于配置需要加载的插件,具体参见插件。
test/** 用于单元测试,具体参见单元测试。
app.js 和 agent.js 用于自定义启动时的初始化工作,可选,具体参见启动自定义。关于agent.js的作用参见Agent机制。
由内置插件约定的目录:
app/public/** 用于放置静态资源,可选,具体参见内置插件 egg-static。
app/schedule/** 用于定时任务,可选,具体参见定时任务。
若需自定义自己的目录规范,参见 Loader API
app/view/** 用于放置模板文件,可选,由模板插件约定,具体参见模板渲染。
app/model/** 用于放置领域模型,可选,由领域类相关插件约定,如 egg-sequelize。
加载顺序
Egg 将应用、框架和插件都称为加载单元(loadUnit);
在加载过程中,Egg会遍历所有的加载单元,加载时有一点的优先级
· 插件 => 框架 => 应用依次加载
· 依赖方先加载
· 框架按继承顺序加载,越底层越先加载
如有这样一个应用配置了如下依赖
app
| ├── plugin2 (依赖 plugin3)
| └── plugin3
└── framework1
| └── plugin1
└── egg
最终的加载顺序为
=> plugin1
=> plugin3
=> plugin2
=> egg
=> framework1
=> app
文件加载顺序
package.json => config => app/extend => app.js => app/service => app/middleware => app/controller => app/router.js
路由(Router)
Router 主要用来描述请求 URL 和具体承担执行动作的 Controller 的对应关系, 框架约定了
app/router.js 文件用于统一所有路由规则。
如何定义 Router
app/router.js 里面定义 URL 路由规则
// app/router.js
module.exports = app => {
const { router, controller } = app;
router.get('/user/:id', controller.user.info);
};
app/controller 目录下面实现 Controller
// app/controller/user.js
class UserController extends Controller {
async info() {
const { ctx } = this;
ctx.body = {
name: `hello ${ctx.params.id}`,
};
}
}
常用的定义路由的方法
module.exports = app => {
const { router, controller } = app;
router.get('/home', controller.home);
router.get('/user/:id', controller.user.page);
router.post('/admin', isAdmin, controller.admin);
router.post('/user', isLoginUser, hasAdminPermission, controller.user.create);
router.post('/api/v1/comments', controller.v1.comments.create); // app/controller/v1/comments.js
};
RESTful 风格的 URL 定义
如果想通过 RESTful 的方式来定义路由, 我们提供了
app.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 里面实现对应的函数就可以了。
router 获取参数的方式
1.**Query String 方式**
// app/router.js
module.exports = app => {
app.router.get('/search', app.controller.search.index);
};
// app/controller/search.js
exports.index = async ctx => {
ctx.body = `search: ${ctx.query.name}`;
};
2.**参数命名方式**
// app/router.js
module.exports = app => {
app.router.get('/user/:id/:name', app.controller.user.info);
};
// app/controller/user.js
exports.info = async ctx => {
ctx.body = `user: ${ctx.params.id}, ${ctx.params.name}`;
};
// curl http://127.0.0.1:7001/user/123/xiaoming
3.**复杂参数的获取**
路由里面也支持定义正则,可以更加灵活的获取参数:
// app/router.js
module.exports = app => {
app.router.get(/^\/package\/([\w-.]+\/[\w-.]+)$/, app.controller.package.detail);
};
// app/controller/package.js
exports.detail = async ctx => {
// 如果请求 URL 被正则匹配, 可以按照捕获分组的顺序,从 ctx.params 中获取。
// 按照下面的用户请求,`ctx.params[0]` 的 内容就是 `egg/1.0.0`
ctx.body = `package:${ctx.params[0]}`;
};
// curl http://127.0.0.1:7001/package/egg/1.0.0
4.**表单内容的获取**
// app/router.js
module.exports = app => {
app.router.post('/form', app.controller.form.post);
};
// app/controller/form.js
exports.post = async ctx => {
ctx.body = `body: ${JSON.stringify(ctx.request.body)}`;
};
// 模拟发起 post 请求。
// curl -X POST http://127.0.0.1:7001/form --data '{"name":"controller"}' --header 'Content-Type:application