JS 流行框架(四):EggJS

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
  • /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
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值