Egg学习(一)

简介:

egg 的定位是企业级 web 基础框架,基于koa封装,旨在帮助开发者孕育适合自己团队的框架。
特点

  • 提供基于 Egg 定制上层框架的能力
  • 高度可扩展的插件机制
  • 内置多进程管理
  • 基于 Koa 开发,性能优异
  • 框架稳定,测试覆盖率高
  • 渐进式开发

 

1.初始化项目

要求: node >= 8 && npm >= 6.1.0

1 初始化项目
npm init
npm i egg egg-bin -D
2 初始化配置文件
// package.json
// 添加npm到package.json
{
   "name":  "egg-example",
   "scripts": {
     "dev": "egg-bin dev"
  }
}
3.相关的内置对象及目录结构

目录结构
内置对象

2. Controller和Router

Controller

作用: Controller主要负责解析用户的输入,处理后返回相应的结果
所有的Controller文件都必须在app/controller目录下访问时可通过目录名级联访问。一般支持两种编写方式类(推荐)方法
下面主要介绍类写法

// app/controller/home.js
const Controller = require('egg').Controller;
class HomeController extends Controller {
    async index() {
        this.ctx.body = 'Hello world!!!';
    }
}

module.exports = HomeController

详解: Controller类继承于egg.Controller。其自带以下几个属性挂在this上:

  • this.ctx: 请求的上下文Context对象的实例。
  • this.app: Application对象的实例。
  • this.service: 可以访问抽象出的业务层,等价于this.ctx.service。
  • this.config: 应用运行时的配置项。
  • this.logger: 可以打印出相关日志,与context logger类似,其还会加上打印日志的文件路径。

基类:可以通过按照类的方式再封装一层,内置一些常用的方法将其作为基类,后续的controller类继承该基类进行操作即可复用其内部的方法。

其中内部的一些操作像获取query/body、设置响应信息等请查看egg-controller文档

Router

作用: 主要用来描述请求URL和具体承担执行动作的Controller的对应关系。
框架规定了app/router.js文件用于统一所有路径规则。

// app/router.js
module.exports = app => {
    const { router, controller } = app;
    router.get('/', controller.home.index);
};

详解: router主要由以下五部分组成:

  • verb: 用户出发动作(get/put/post/pach/redirect等)
  • router-name(可选): 路由设定的别名,可通过Helper的辅助函数生产URL
  • path-match: 路由URL路径
  • middleware(可选): 可配置多个middleware(后面有介绍)。
  • controller: 指定具体的controller。

更多详细用法可参考egg-router文档

解析: 通过结合上面的control文件,可以根据其目录名级联关系(home.js)来访问里面定义的index方法。这时候已经完成了最基本的egg编写,可以运行并查看结果

// 运行egg项目
npm run dev

打开浏览器http://localhost:7001即可查看效果。
 

3.中间件

因为koa是基于koa实现的,所有中间件形式和koa的中间件形式是一样的,都属于洋葱模型。

// app/middleware/robot.js
// options === app.config.robot
// 该中间件用于禁用百度爬虫
module.exports = (options, app) => {
  return async function robotMiddleware(ctx, next) {
    const source = ctx.get('user-agent') || '';
    const match = options.ua.some(ua => ua.test(source));
    if (match) {
      ctx.status = 403;
      ctx.message = 'Go away, robot.';
    } else {
      await next();
    }
  }
};

// config/config.default.js
// 注册中间件,决定middleware的顺序
exports.middleware = [
  'robot'
];
// robot's configurations
exports.robot = {
  ua: [
    /Baiduspider/i
  ]
};

我们约定一个中间件一般放置在app/middleware目录下的单独文件,需要export一个普通的function,接受两个参数
参数:
options: 中间件的配置项,框架会将 app.config[${middlewareName}] 传递进来。
app: 当前应用 Application 的实例。
 
全局中间件: 需要在config.default.js中加入配置。如在框架或插件中不支持config中配置,则可以通过app.config.coreMiddleware.unshift(中间件名)来添加中间件。
路由中使用: 路由中可以单独实例化并挂载,操作如下:

// app/router.js
module.export = app => {
  const robot = app.middleware.root({ua:[/Baiduspider/i]});
  app.router.get('/news', robot, app.controller.handler);
}

默认中间件: 一般框架自身会带有默认中间件,我们可以在config.default.js中通过其名称修改其中间件的配置。但是不可妄想配置应用层同名中间件来覆盖其默认中间件。因为:框架和插件的默认中间件会在应用层配置的中间件之前,不会被应用层中间件覆盖,如果应用层有自定义同名中间件,在启动时会报错
通用配置: 可以通过配置来决定是否启用中间件,包括禁用默认中间件操作等。配置参数包括以下三个:

  • enable: 控制中间件是否开启。
  • match:设置只有符合某些规则的请求才会经过该中间件。
  • ignore:设置符合某些规则不经过中间件。

match和ignore均支持字符串(以配置字符串为前缀的url都会匹配上,也支持字符串数组)、正则(匹配url路径)、函数(ctx作为参数,返回值判断),match和ignore不能同时配置

4.插件

插件和中间件:

  • 中间件加载有先后顺序,但是中间件自身无法管理这顺序。
  • 中间件定位是拦截用户请求,并在它前后做一些事,更多一些逻辑处理等需要插件完成。
  • 一些非常复杂的初始化逻辑,需在启动时完成,这显然不合适放到中间件去实现。
    1.插件安装
// 安装
npm i egg-mysql --save

或在package.json配置

{
  "dependencies": {
    "egg-mysql": "^3.0.0"
  }
}

注意:我们建议通过 ^ 的方式引入依赖,并且强烈不建议锁定版本。

2. 插件声明

// 在config/plugin.js中生命
exports.nunjucks = {
  enable: true,
  package: 'egg-view-nunjucks'
};

参数:

  • {Boolean} enable: 是否开启插件,默认为true
  • {String} page: 模块名称,
  • {String} path: 插件的绝对路径
  • {Array} env: 需要在指定环境中开启,会覆盖插件自身package.json中的配置
  1. 插件使用及配置
    插件一般包含自己默认配置,也可在config.default.js覆盖原来的配置。
// config/config.default.js
exports.mysql = {
  client: {
    host: 'mysql.com',
    port: '3306',
    user: 'test_user',
    password: 'test_password',
    database: 'test',
  },
};
app.mysql.query(sql, values);

 

5.模版渲染

这类主要用到的是Nunjucks来渲染。详情可参考Nunjucks文档

安装和配置
// 安装
npm i egg-view-nunjucks --save
       // 启动插件
// config/plugin.js
exports.nunjucks = {
  enable: true,
  package: 'egg-view-nunjucks'
};

// 添加默认配置
// config/config.default.js
exports.view = {
  defaultViewEngine: 'nunjucks',
  mapping: {
    '.tpl': 'nunjucks',
  },
};

后续详情可参考egg模版解析

6.service

作用: Controller一般都不会产出数据,复杂的处理过程应抽象为业务逻辑层Service。
优点
1.使controller中逻辑更简洁。
2.代码独立性和复用性。
3.逻辑分离,更容易编写测试用例。
4.同时Service是懒加载的,只有当访问它的时候框架才会去实例化它。

// app/service/news.js
const Service = require('egg').Service;

class NewsService extends Service {
  async list(page = 1) {
    const newList = []
    // 逻辑操作
    return newsList;
  }
}
module.exports = NewsService;

// app/controller/news.js
...
const newsList = ctx.service.news.list(3)
...

将复杂的逻辑放到service里,controller可通过service[文件名]获取到相应的属性或方法,和router获取controller雷同。
 

7.框架扩展

开发中我们可以扩展已有的API的熟悉来方便开发

  • Application
  • Context
  • Request
  • Response
  • Helper
方法扩展
// app/extend/application.js
module.exports = {
 foo(param) {
   // this 就是 app 对象,在其中可以调用 app 上的其他方法,或访问属性
 },
};
属性扩展
// app/extend/application.js
const BAR = Symbol('Application#bar');

module.exports = {
  get bar() {
    // this 就是 app 对象,在其中可以调用 app 上的其他方法,或访问属性
    if (!this[BAR]) {
      // 实际情况肯定更复杂
      this[BAR] = this.config.xx + this.config.yy;
    }
    return this[BAR];
  },
};

 

8.启动自定义

框架提供了统一的入口文件(app.js)进行启动过程自定了,以及提供了一些生命周期钩子供开发人员处理。

  • configWillLoad: 皮质文件即将加载,这是最后动态修改配置的时机
  • configDidLoad: 配置文件加载完成
  • didLoad: 文件加载完成
  • willReady: 插件启动完毕
  • didReady: worker 准备就绪
  • serverDidReady: 应用启动完成
  • beforeClose: 应用即将关闭
// app.js
class AppBootHook {
    constructor(app) {
      this.app = app;
    }
    async serverDidReady() {
      // http / https server 已启动,开始接受外部请求
      // 此时可以从 app.server 拿到 server 的实例
  
      this.app.server.on('timeout', socket => {
        // handle socket timeout
      });
    }
}
module.exports = AppBootHook;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值