JS 流行框架(四):EggJS
Egg 是阿里巴巴基于 Koa 的有约束和规范的企业级 Web 开发框架,基于 Egg 的项目目录结构和名称有严格的规定,和 ESLint 一样,如果不符合规定那么项目将无法运行,此外,Egg 基于 MVC 的架构模式,M —— Model 层负责应用程序的数据逻辑部分,类似于 Service、V —— View 层负责应用程序的数据显示部分(静态/动态网页),类似于 Router、C —— Controller 层负责应用程序的业务逻辑部分,将数据和页面关联
基本使用
在使用 EggJS 之前,必须先下载 egg 和 egg-bin 模块,前者是 EggJS 的核心模块,后者是用于本地开发调试的模块,示例如下:
npm install egg --save
npm install egg-bin --save-dev
在项目下载完成之后,可以在 package.json 文件中的 script 属性中添加 dev 命令以便于快速启动项目
在基于 EggJS 项目中,目录结构有严格的限制,以下是 Egg 项目中最简单的结构
- /app
- /controller
- home.js
- router.js
- /controller
- /config
- config.default.js
在项目中,目录 app 专门用于保存项目的核心代码,子目录 controller 用于保存程序业务逻辑相关的代码,home.js 专门用于编写处理主页的业务逻辑代码,router.js 专门用于编写路由相关的代码,目录 config 专门用于保存项目的配置文件,config.default.js 用于保存项目的默认配置,此外,必须注意的是,除了 controller 目录下的文件之外,上述目录和文件的名称不能擅自修改,否则会报错,每个文件的内容如下所示:
- config.default.js
module.exports = {
keys: 'org.xin.*?'
}
此处的 keys 即项目用于生成 Cookie 时所用的密钥
- home.js
const Controller = require('egg').Controller;
class HomeController extends Controller {
async index() {
this.ctx.body = 'Hello World!';
}
}
module.exports = HomeController;
上述示例中,定义了一个继承自 Controller 的 HomeController 类,此类的所有实例方法即用于处理主页相关的业务逻辑,这些方法必须被标记为 async 异步方法,此外,凡是继承 Controller 类的类中的 this 拥有如下属性:
| 属性 | 含义 |
|---|---|
| this.ctx | 当前请求的 Context 实例,类似于 Koa 中的 ctx |
| this.app | 当前应用的 Application 实例,利用此实例可以获取框架提供的全局实例和方法 |
| this.service | 应用定义的 Service 实例,利用此实例可以访问应用中 Service 层的方法 |
| this.config | 应用运行时和配置相关的选项 |
| this.logger | 应用提供的 logger 实例,用于记录日志,之后再详细说明 |
- router.js
module.exports = app => {
// 从 app 中解构出 router 和 controller
const {
router, controller} = app;
/**
* 利用 router 的 get 方法处理根路由(类似于 Koa2 中的 router),在此方法中
* 第一个参数说明路由
* 第二个参数说明处理此路由的方法,此方法来自 controller 目录下的 home.js 所暴露的类中的 index 实例方法
* (即 controller 中保存了 controller 目录下的所有内容,访问 controller 就相当于访问 controller 目录)
*/
router.get('/', controller.home.index);
}
上述示例中,向外界暴露了一个方法,此方法接收一个名称为 app 的参数,此参数即为当前应用的 Application 实例,此实例中拥有如下属性:
| 属性 | 含义 |
|---|---|
| env | 运行环境 |
| name | 项目名称 |
| baseDir | 项目目录 |
| controller | 保存了项目中 controller 目录下的所有内容 |
| loggers | 用于日志记录 |
| middlewares | 保存了项目中的所有中间件 |
| router | 用于路由 |
参数
EggJS 项目中获取请求参数的方式和 Koa 基本相同,示例如下:
- /controller/user.js
const Controller = require('egg').Controller;
class UserController extends Controller {
async getQuery() {
this.ctx.body = this.ctx.query;
}
async getParams() {
this.ctx.body = this.ctx.params;
}
async getBody() {
this.ctx.body = this.ctx.request.body;
}
}
module.exports = UserController;
- router.js
module.exports = app => {
// 从 app 中解构出 router 和 controller
const {
router, controller} = app;
// 处理静态 get 请求
router.get('/user/info', controller.user.getQuery);
// 处理动态 get 请求
router.get('/user/register/:username/:password', controller.user.getParams);
// 处理 post 请求
router.post('/user/login', controller.user.getBody);
}
默认情况下,上述示例中的 post 请求将失败,原因在于 EggJS 拥有一套安全机制,默认的 post 请求被认为是不安全的,所以必须在 config.default.js 中将此机制忽略,示例如下:
module.exports = {
keys: 'org.xin.*?',
security: {
csrf: {
ignoreJSON: true // 默认为 false,当设置为 true 时,将会放过所有 content-type 为 `application/json` 的请求
}
}
}
资源
静态
在基于 EggJS 的项目中,不用专门处理静态资源,因为 EggJS 已经处理好了,只需要将所有的静态资源存放在 app 目录下的 public 子目录下即可,此处不再详细说明,必须注意的是,目录名称必须为 public,且必须在 app 目录下,在通过浏览器访问静态资源时,必须加上 /public,否则将出现 404 错误,示例如下:
http://127.0.0.1:7001/public/login.html
动态
在基于 EggJS 的项目中,通过插件处理动态资源,在 EggJS 中,插件就是特殊的中间件,专门用于处理那些和请求无关的独立的业务逻辑,要想使用插件处理动态资源,必须先下载此插件,示例如下:
npm install egg-view-ejs --save
在下载完成之后,在 config 目录下新建一个 plugin.js 文件,用以描述插件相关的配置,示例如下:
module.exports = {
ejs: {
enable: true,
package: 'egg-view-ejs'
}
}
之后在 config.default.js 中新增如下配置:
module.exports = {
view: {
mapping: {
'.html': 'ejs'
}
}
}
之后,必须在 app 目录下创建 view 子目录以存放动态资源,资源以 html 为扩展名,然后就可以在 controller 中利用 ctx 的 render 方法渲染动态资源,示例如下:
const Controller = require('egg').Controller;
class HomeController extends Controller {
async index() {
await this.ctx.render('index', {
username: 'Reyn Morales',
password: 1024
})
}
}
module.exports = HomeController;
网络数据
在基于 EggJS 的项目中,不论是数据库中的数据还是临时请求网络中的数据,均在 Service 中处理,所以我们必须在 app 目录下新建一个 service 子目录(目录名称不能擅自修改),此目录专门用于保存数据逻辑相关的代码,此处说明如何临时请求网络上的数据,关于数据库相关的内容将在说明如何在 EggJS 中使用 MySQL 和 Sequelize 时说明,示例如下:
- /service/home.js
const Service = require('egg').Service;
class HomeService extends Service {
async getNews() {
const response = await this.ctx.curl('http://127.0.0.1:3000/getNewsByGetWithoutParams');
return JSON.parse(response.data);
}
}
module.exports = HomeService;
和 Controller 类似,凡是继承 Service 类的类中的 this 也将拥有和 Controller 类中 this 所拥有的属性,此外还包含一些额外的属性,内容如下:
| 属性 | 含义 |
|---|---|
| this.ctx.curl | 发起网络调用 |
| this.ctx.service.otherService | 调用其它 Service |
| this.ctx.db | 发起数据库调用,db 可能是其它插件提前挂载到 app 上的模块 |
实际上 curl 方法可以发起多种网络调用,示例如下:
const Service = require('egg').Service;
class HomeService extends Service {
async getNews() {
/* 不带参数的 get 请求 */
// const response = await this.ctx.curl('http://127.0.0.1:3000/getNewsByGetWithoutParams');
/* 带参数的 get 请求 */
// const response = await this.ctx.curl('http://127.0.0.1:3000/getNewsByGetWithParams?title=SXU');
/* 不带参数的 post 请求 */
// const response = await this.ctx.curl('http://127.0.0.1:3000/getNewsByPostWithoutParams', {
// method: 'post'
// });
/* 带参数的 post 请求 */
const response = await this.ctx.curl('http://127.0.0.1:3000/getNewsByPostWithParams', {
method: 'post',
data: {
name: 'Reyn Morales',
age: 21
}
});
return JSON.parse(response.data);
}
}
module.exports = HomeService;
- /controll

最低0.47元/天 解锁文章
2257

被折叠的 条评论
为什么被折叠?



