🌟前言
哈喽小伙伴们,新的专栏 Node 已开启;这个专栏里边会收录一些Node的基础知识和项目实战;今天我们继续带大家了解Node的框架 Egg ;让我们一起来看看吧🤘
🌟 框架内置对象
框架中内置的一些基础对象:
类型 | 描述 |
Koa 继承对象 | Application, Context, Request, Response |
框架扩展对象 | Controller, Service, Helper, Config, Logger |
🌟Koa 继承对象
🌟Koa Application
Application
是全局应用对象,在一个应用中,只会实例化一个
,它继承自 Koa.Application
,在它上面我们可以挂载一些全局的方法和对象。我们可以轻松的在插件或者应用中扩展 Application对象。
🌟事件
在框架运行时,会在 Application 实例上触发一些事件,应用开发者或者插件开发者可以监听这些事件做一些操作。作为应用开发者,我们一般会在启动自定义脚本中进行监听。
事件 |
描述 |
server |
该事件一个 worker 进程只会触发一次,在 HTTP 服务完成启动后,会将 HTTP server 通过这个事件暴露出来给开发者。 |
error |
运行时有任何的异常被 onerror 插件捕获后,都会触发 error 事件,将错误对象和关联的上下文(如果有)暴露给开发者,可以进行自定义的日志记录上报等处理。 |
request 和 response |
应用收到请求和响应请求时,分别会触发 request 和 response 事件,并将当前请求上下文暴露出来,开发者可以监听这两个事件来进行日志记录。 |
// app.js module.exports = app => { app.once('server', server => { // websocket }); app.on('error', (err, ctx) => { // report error }); app.on('request', ctx => { // log receive request }); app.on('response', ctx => { // ctx.starttime is set by framework const used = Date.now() - ctx.starttime; // log total cost }); };
🌟获取全局应用对象的方式
Application 对象(全局应用对象)几乎可以在编写应用时的任何一个地方获取到,下面介绍几个经常用到的获取方式:
几乎所有被框架 Loader 加载的文件(Controller,Service,Schedule 等),都可以 export 一个函数,这个函数会被 Loader 调用,并使用 app 作为参数:
启动自定义脚本
// app.js module.exports = app => { // app app.cache = new Cache(); };
Controller、Service文件 在继承于 Controller, Service 基类的实例中,可以通过 this.app 访问到 Application 对象。
// app/controller/user.js class UserController extends Controller { async fetch() { // this.app this.ctx.body = this.app.cache.get(this.ctx.query.id); } }
和 Koa 一样,在 Context 对象上,可以通过 ctx.app 访问到 Application 对象。以上面的 Controller 文件举例:
// app/controller/user.js class UserController extends Controller { async fetch() { this.ctx.body = this.ctx.app.cache.get(this.ctx.query.id); } }
🌟全局应用对象的属性
属性 |
类型 |
描述 |
app.baseDir |
String |
应用程序的根目录 |
app.config |
Object |
应用程序的配置 |
app.env |
String |
app.env 等同于 app.config.env |
app.plugins |
Object |
应用程序的配置 |
app.middleware |
Object |
中间件列表 |
app.controller |
Object |
控制器列表 |
app.router |
Object |
获取路由对象 |
app.locals |
Object |
View的全局变量 |
🌟Context
Context 是一个请求级别的对象,继承自 Koa.Context。在每一次收到用户请求时,框架会实例化一个 Context 对象,这个对象封装了这次用户请求的信息,并提供了许多便捷的方法来获取请求参数或者设置响应信息。框架会将所有的 Service 挂载到 Context 实例上,一些插件也会将一些其他的方法和对象挂载到它上面(egg-sequelize 会将所有的 model 挂载在 Context 上)。
🌟获取Context的方式
最常见的 Context 实例获取方式是在 Middleware, Controller 以及 Service 中。Controller 中的获取方式在上面的例子中已经展示过了,在 Service 中获取和 Controller 中获取的方式一样,在 Middleware 中获取 Context 实例则和 Koa 框架在中间件中获取 Context 对象的方式一致。
Controller 中的获取Context:
// app/controller/user.js class UserController extends Controller { async fetch() { // this.ctx this.ctx.body = this.app.cache.get(this.ctx.query.id); } }
在 Service 中获取和 Controller 中获取的方式一样:
// app/service/news.js const Service = require('egg').Service; class NewsService extends Service { async list(page = 1) { // this.ctx 获取Context } }
框架的 Middleware 同时支持 Koa v1 和 Koa v2 两种不同的中间件写法,根据不同的写法,获取 Context 实例的方式也稍有不同:
// Koa v1 function* middleware(next) { // this is instance of Context console.log(this.query); yield next; } // Koa v2 async function middleware(ctx, next) { // ctx is instance of Context console.log(ctx.query); }
在定时任务中的每一个 task 都接受一个 Context 实例作为参数,以便我们更方便的执行一些定时的业务逻辑:
// app/schedule/refresh.js exports.task = async ctx => { await ctx.service.posts.refresh(); };
🌟Request & Response
Request 是一个请求级别的对象,继承自 Koa.Request。封装了 Node.js 原生的 HTTP Request 对象,提供了一系列辅助方法获取 HTTP 请求常用参数。
Response 是一个请求级别的对象,继承自 Koa.Response。封装了 Node.js 原生的 HTTP Response 对象,提供了一系列辅助方法设置 HTTP 响应。
🌟获取方式
可以在 Context 的实例上获取到当前请求的 Request(ctx.request) 和 Response(ctx.response) 实例。
// app/controller/user.js class UserController extends Controller { async fetch() { const { app, ctx } = this; const id = ctx.request.query.id; ctx.response.body = app.cache.get(id); } }
Koa 会在 Context 上代理一部分 Request 和 Response 上的方法和属性,参见 Koa.Context。
如上面例子中的 ctx.request.query.id 和 ctx.query.id 是等价的,ctx.response.body= 和 ctx.body= 是等价的。
需要注意的是,获取 POST 的 body 应该使用 ctx.request.body,而不是 ctx.body。
🌟框架扩展对象
🌟Controller
框架提供了一个 Controller 基类,并推荐所有的 Controller 都继承于该基类实现。这个 Controller 基类有下列属性:
ctx - 当前请求的 Context 实例。
app - 应用的 Application 实例。
config - 应用的配置。
service - 应用所有的 service。
logger - 为当前 controller 封装的 logger 对象。
在 Controller 文件中,可以通过两种方式来引用 Controller 基类:
// app/controller/user.js // 从 egg 上获取(推荐) const Controller = require('egg').Controller; class UserController extends Controller { // implement } module.exports = UserController; // 从 app 实例上获取 module.exports = app => { return class UserController extends app.Controller { // implement }; };
🌟Service
框架提供了一个 Service 基类,并推荐所有的 Service 都继承于该基类实现。
Service 基类的属性和 Controller 基类属性一致,访问方式也类似:
// app/service/user.js // 从 egg 上获取(推荐) const Service = require('egg').Service; class UserService extends Service { // implement } module.exports = UserService; // 从 app 实例上获取 module.exports = app => { return class UserService extends app.Service { // implement }; };
🌟Helper
Helper 用来提供一些实用的 utility 函数。它的作用在于我们可以将一些常用的动作抽离在 helper.js 里面成为一个独立的函数,这样可以用 JavaScript 来写复杂的逻辑,避免逻辑分散各处,同时可以更好的编写测试用例。
Helper 自身是一个类,有和 Controller 基类一样的属性,它也会在每次请求时进行实例化,因此 Helper 上的所有函数也能获取到当前请求相关的上下文信息。
🌟获取方式
可以在 Context 的实例上获取到当前请求的 Helper(ctx.helper) 实例。
// app/controller/user.js class UserController extends Controller { async fetch() { const { app, ctx } = this; const id = ctx.query.id; const user = app.cache.get(id); ctx.body = ctx.helper.formatUser(user); } }
除此之外,Helper 的实例还可以在模板中获取到,例如可以在模板中获取到 security 插件提供的 shtml 方法。
// app/view/home.nj {{ helper.shtml(value) }}
🌟自定义 helper 方法
应用开发中,我们可能经常要自定义一些 helper 方法,例如上面例子中的 formatUser,我们可以通过框架扩展的形式来自定义 helper 方法。
// app/extend/helper.js module.exports = { formatUser(user) { return only(user, ['name', 'phone']); }, };
🌟Config
我们推荐应用开发遵循配置和代码分离的原则,将一些需要硬编码的业务配置都放到配置文件中,同时配置文件支持各个不同的运行环境使用不同的配置,使用起来也非常方便,所有框架、插件和应用级别的配置都可以通过 Config 对象获取到
🌟获取方式
我们可以通过 app.config 从 Application 实例上获取到 config 对象,在 Controller, Service, Helper 的实例上通过 this.config 获取到 config 对象。
🌟Logger
框架内置了功能强大的日志功能,可以非常方便的打印各种级别的日志到对应的日志文件中,每一个 logger 对象都提供了 4 个级别的方法:
logger.debug()
logger.info()
logger.warn()
logger.error()