课程名称:web前端架构师
课程章节:第14周 第三章
主讲老师:张轩
课程内容:eggjs 基础知识
eggjs 目录结构
对应文档地址。https://www.eggjs.org/zh-CN/basics/structure
由框架约定的目录:
- app/router.js 用于配置 URL 路由规则。
- app/controller/** 用于解析用户的输入,处理后返回相应的结果。
- app/service/** 用于编写业务逻辑层,可选,建议使用。
- app/middleware/** 用于编写中间件,可选。
- app/public/** 用于放置静态资源,可选,具体参见内置插件 egg-static。
- app/extend/** 用于框架的扩展,可选。
- config/config.{env}.js 用于编写配置文件。
- config/plugin.js 用于配置需要加载的插件。
- test/** 用于单元测试,具体参见单元测试。
- app.js 和 agent.js 用于自定义启动时的初始化工作,可选
我们开发主要关注的就是 app 这个目录和 config 这个目录
- app/router.js 用于配置 URL 路由规则
- app/controller/** 用于解析用户的输入,处理后返回相应的结果
- app/service/** 用于编写业务逻辑层,可选,建议使用
router
// app/router.ts
import { Application } from 'egg';
export default (app: Application) => {
const { controller, router } = app;
router.get('/', controller.home.index);
}
controller
// app/controller/home.ts
import { Controller } from 'egg';
export default class HomeController extends Controller {
public async index() {
const { ctx } = this;
ctx.body = await ctx.service.test.sayHi('egg');
}
}
Service
// app/service/Test.ts
import { Service } from 'egg';
/**
* Test Service
*/
export default class Test extends Service {
/**
* sayHi to you
* @param name - your name
*/
public async sayHi(name: string) {
return `hi, ${name}`;
}
}
Controller
定义的 Controller 类,会在每一个请求访问到 server 时实例化一个全新的对象,而项目中的 Controller 类继承于 egg.Controller,会有下面几个属性挂在 this 上。
- this.ctx: 当前请求的上下文 Context 对象的实例,通过它我们可以拿到框架封装好的处理当前请求的各种便捷属性和方法。
- this.app: 当前应用 Application 对象的实例,通过它我们可以拿到框架提供的全局对象和方法。
- this.service:应用定义的 Service,通过它我们可以访问到抽象出的业务层,等价于 this.ctx.service 。
- this.config:应用运行时的配置项。
- this.logger:logger 对象,上面有四个方法(debug,info,warn,error),分别代表打印四个不同级别的日志,使用方法和效果与 context logger 中介绍的一样,但是通过这个 logger 对象记录的日志,在日志前面会加上打印该日志的文件路径,以便快速定位日志打印位置。
编写一个 controller
编写一个 controller,设置 controller 的响应
import { Controller } from 'egg';
export default class ArticleController extends Controller {
async index() {
const { ctx } = this;
ctx.body = {
data: 'article',
};
}
}
然后在路由中配置
import { Application } from 'egg';
export default (app: Application) => {
const { controller, router } = app;
...
router.get('/article', controller.article.index);
};
然后就可以在浏览器通过 /article 访问了
解析请求参数
- query 可以通过 ctx.query 获取
- params 可以通过 ctx.params 获取
- body 可以通过 ctx.request.body 获取
import { Controller } from 'egg';
export default class ArticleController extends Controller {
async index() {
const { ctx } = this;
ctx.body = {
data: 'article',
quert: ctx.query,
params: ctx.params,
body: ctx.request.body,
};
}
}
import { Application } from 'egg';
export default (app: Application) => {
const { controller, router } = app;
router.get('/article', controller.article.index);
router.get('/article/:id', controller.article.index);
router.post('/article', controller.article.index);
};
服务 Service
在开发的过程中,我们要保持 Ctroller 的简洁,当我们的业务逻辑比较复杂时,就需要使用 service 了
简单来说,Service 就是在复杂业务场景下用于做业务逻辑封装的一个抽象层,提供这个抽象有以下几个好处:
- 保持 Controller 中的逻辑更加简洁。
- 保持业务逻辑的独立性,抽象出来的 Service 可以被多个 Controller 重复调用。
- 将逻辑和展现分离,更容易编写测试用例,测试用例的编写具体可以查看这里。
使用场景
- 复杂数据的处理,比如要展现的信息需要从数据库获取,还要经过一定的规则计算,才能返回用户显示。或者计算完成后,更新到数据库。
- 第三方服务的调用,比如 GitHub 信息获取等。
使用 Service
编写一个 service。 比如我们要发送一个第三方的一个 http 请求
import { Service } from 'egg';
export default class DogService extends Service {
async show<T>() {
const res = await this.ctx.curl<T>('https://dog.ceo/api/breeds/image/random', {
dataType: 'json',
});
return res.data;
}
}
在 controller 中使用
import { Controller } from 'egg';
export default class ArticleController extends Controller {
async dog() {
const { service, ctx } = this;
const res = await service.dog.show<{
message: string
status: string
}>();
ctx.body = {
data: res.message,
};
}
}