5分钟快速入门 Egg.js
前言
Egg 是阿里基于 Koa 的有约束和规范的企业级 web 开发框架。对比 Express 和 Koa ,Express 是基于 ES5 的 web 开发框架,Koa2 是 Express 原班人马打造的基于 ES7 的 web 开发框架。
为什么说 Egg 在阿里的大量企业级项目中会被广泛应用,这是因为 Egg 本身是有约束和规范的,大大降低团队的沟通成本和项目的维护成本,Express 和 Koa 没有约束和规范,导致团队的沟通成本和项目的维护成本变高。
本篇就以如何快速上手 Egg 为例,来建立一个初始项目。对于有关 Egg 的更多信息和功能使用请参照 Egg 官网进行学习,官方文档写的非常详细,具体内容就不在这里展示。
快速上手
安装使用
在实际项目中,官方推荐我们使用 Egg 提供的脚手架工具来快速创建项目,但是为了更好的了解 Egg,我们跳过脚手架帮我们初始化项目,选择手动创建项目的方式。
安装依赖
首先,创建一个项目文件夹,在终端命令行输入 npm init
初始化 package.json
。
然后,继续在终端命令行输入 npm i egg --save
和 npm i egg-bin --save-dev
安装 Egg 核心依赖包。
最后,在 package.json
中添加以下脚本命令并指定端口,防止 Egg 默认的端口与现有进程端口冲突。
"scripts": {
"dev": "egg-bin dev --port 3000"
}
添加控制器和路由
在项目中创建控制器和路由,编写 Controller 和 Router,新建 app/controller/home.js
文件,在该文件中添加如下代码。
// app/controller/home.js
const Controller = require('egg').Controller
class HomeController extends Controller {
async index() {
this.ctx.body = 'Hello Egg'
}
}
module.exports = HomeController
值得注意的是,上述代码中,Egg 会自动给控制器的 this
挂载一些属性。其中就包括 this.ctx
, 表示当前请求的上下文 Context 对象的实例,通过它我们可以拿到框架封装好的处理当前请求的各种便捷属性和方法。
配置路由映射
配置路由映射需要创建 app/router.js
文件,在该文件中添加如下代码。
// app/router.js
module.exports = app => {
// 从服务端的实例对象中解构出处理路由的对象和处理控制器的对象
const { router, controller } = app
// 利用处理路由的对象监听路由的请求
router.get('/', controller.home.index)
}
值得注意的是,上述代码中,在 router.js
中必须暴露出去一个方法,这个方法接收一个参数,这个参数就是服务端的实例对象。
在 Egg 中不用导入控制器,只要获取到从服务器实例中解构出来的控制器对象,就相当于获取了控制器目录 controller
,我们就可以通过点语法拿到这个目录中的文件和方法。
添加配置文件
添加配置文件需要创建 config/config.default.js
文件,在该文件中添加如下代码。
exports.keys = '<此处改为你自己的 Cookie 安全字符串>' // 用于生成客户端中保存的userId
这时候,项目的目录结构应该是这样的。
egg-example
├── app
│ ├── controller
│ │ └── home.js
│ └── router.js
├── config
│ └── config.default.js
└── package.json
最后,在终端命令行输入 npm run dev
启动项目。

在浏览器中输入 http://localhost:3000 成功显示上述代码中返回到页面中的内容。

处理请求参数
在 Egg 中处理请求参数,直接在 controller 目录中创建对应的方法,然后在 app/router.js
文件中处理 get 和 post 请求参数。
例如,在 app/controller/home.js
文件中增加如下代码。
async getQuery() {
// 获取传统get请求参数
// this.ctx.request.query
let query = this.ctx.query
this.ctx.body = query
}
async getParams() {
// 获取动态路由形式的get请求参数
let params = this.ctx.params
this.ctx.body = params
}
async getBody() {
// 获取post请求参数
let body = this.ctx.request.body
this.ctx.body = body
}
在 app/router.js
文件中处理对应的 get 和 post 请求参数,增加如下代码。
router.get('/user', controller.home.getQuery)
router.get('/register/:name/:age', controller.home.getParams)
router.post('/login', controller.home.getBody)
在浏览器输入 http://localhost:3000/user?name=lisi&age=21 ,就会在页面显示正确的内容。

在浏览器输入 http://localhost:3000/register/zhangsan/22 ,同样会在页面显示正确的内容。

处理静态资源
在 Egg 中如何处理静态资源,我们什么都不用配置,Egg 内置了 static 插件,线上环境官网建议我们部署到 CDN,无需该插件。static
插件默认映射 /public/* -> app/public/*
目录。
在项目根目录创建 public 文件夹,可以在文件夹中增加一些静态文件,如增加一张图片,然后进行测试。
在浏览器中输入 http://localhost:3000/public/egg.png ,就能访问到指定的文件。

处理动态资源
在 Egg 中处理动态资源需要使用插件 egg-view-ejs
,在终端命令行输入 npm i egg-view-ejs
安装插件,其实插件在 Egg 中的作用就是特殊的中间件,用来处理那些和请求无关独立的业务逻辑。
安装完插件包之后,在 config
目录下新建 plugin.js
文件,在该文件中输入如下代码。
exports.ejs = {
enable: true,
package: 'egg-view-ejs'
}
同时在 config/config.default.js
文件中新增如下配置。
view: {
mapping: {
'.html': 'ejs'
}
}
在 app 目录中新建 view 目录,用于存放动态网页,例如,在 app/view
文件夹中新建 index.html
文件,然后在该文件中增加如下内容。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>动态网页</title>
</head>
<body>
<h1>我是动态网页</h1>
<h2><%=msg%></h2>
</body>
</html>
最后在控制器中通过上下文 render
方法渲染,在 app/controller/home.js
文件中新增如下方法。
async getHome(){
await this.ctx.render('index', {msg:'Hello Egg'});
}
通过在 app/router.js
中增加 router.get('/home', controller.home.getHome)
这行代码来处理动态资源。
在浏览器中输入 http://localhost:3000/home ,就能在页面上展现出动态网页的内容。

编写中间件
Egg 的中间件是基于 Koa 的, 所以编写 Egg 的中间件形式和 Koa 的中间件形式是一样的,但是 Egg 规定我们需要将中间件写到特殊的目录中,并且提供多种使用方式,更多信息可参考官网中间件这一部分内容。
创建 app/middleware
文件,然后在此文件夹中编写中间件,例如新建 clientCheck.js
文件,用来判断当前浏览器是否为谷歌浏览器,在此文件中书写以下内容。
module.exports = (options, app) => {
return async (ctx, next) => {
// 1.获取客户端的请求信息
let userAgent = ctx.get('user-agent')
// 2.判断客户端是否是谷歌浏览器
let flag = options.ua.test(userAgent)
if (flag) {
ctx.status = 401
ctx.body = '不支持当前的浏览器'
} else {
next()
}
}
}
然后在 app.router.js
文件中引入此中间件,注册到对应的路由处理程序中,新增如下代码。
let clientCheck = app.middleware.clientCheck({ ua: /Chrome/ })
router.get('/home', clientCheck, controller.home.getHome)
在浏览器中输入 http://localhost:3000/home ,返回以下内容,说明中间件已生效。

编写服务
在 Egg 中编写服务,需要创建 app/service
文件夹,在此文件夹中编写有关服务的接口。
需要注意的是,service
目录必须放在 app 目录中,同时,service
目录支持多级目录,如果是多级目录,那么在调用的时候可以使用链式调用,service
中的 js 文件,如果是以 _
或者首字母都是大写,那么在调用的时候必须转换成驼峰命名。
例如,在 app/service
目录中新增 home.js
文件,以访问猫眼接口为例,在此文件中添加如下代码。
const Service = require('egg').Service
class HomeService extends Service {
async findMovies() {
let response = await this.ctx.curl('https://m.maoyan.com/ajax/movieOnInfoList', {
method: 'get'
})
let data = JSON.parse(response.data)
console.log('HomeService', data)
return data
}
}
module.exports = HomeService
然后在控制器中调用此服务,在 app/controller/home.js
文件中新增如下方法。
async getMovieList() {
let data = await this.ctx.service.home.findMovies()
this.ctx.body = data.movieList
}
在 app.router.js
文件新增 router.get('/movies', controller.home.getMovieList)
这行代码来访问控制器中的方法。
在浏览器输入 http://localhost:3000/movies ,就能访问到对应的数据。

总结
通过上述内容的学习,我想必大家对 Egg 有了一个初步的了解,正是 Egg 提供的这种约束和规范,我们才能在进行团队开发时降低沟通成本和项目维护的成本。同时,Egg 也提供了丰富的功能和灵活的扩展性,使得开发者可以更加高效地开发企业级应用。
转载自:https://juejin.cn/post/7368079196444803083