Eggjs入门系列-基础全面讲解(下)

回顾一下上篇讲到的内容,上篇讲了:运行环境Config 配置中间件(Middleware)路由控制器(Controller)服务(Service) Service 就是在...
摘要由CSDN通过智能技术生成

回顾一下上篇讲到的内容,上篇讲了:

  • 运行环境

  • Config 配置

  • 中间件(Middleware)

  • 路由

  • 控制器(Controller)

服务(Service)

Service 就是在复杂业务场景下用于做业务逻辑封装的一个抽象层

使用场景

  • 复杂数据的处理,比如要展现的信息需要从数据库获取,还要经过一定的规则计算,才能返回用户显示。或者计算完成后,更新到数据库。

  • 第三方服务的调用,比如 GitHub 信息获取等。

定义 Service

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

class UserService extends Service {
  async find(uid) {
    const user = await this.ctx.db.query('select * from user where uid = ?', uid);
    return user;
  }
}

module.exports = UserService;
属性

每一次用户请求,框架都会实例化对应的 Service 实例,由于它继承于 egg.Service,故拥有下列属性方便我们进行开发:

  • this.ctx: 当前请求的上下文 Context 对象的实例

  • this.app: 当前应用 Application 对象的实例

  • this.service:应用定义的 Service

  • this.config:应用运行时的配置项

  • this.logger:logger 对象,上面有四个方法(debuginfowarnerror),分别代表打印四个不同级别的日志,使用方法和效果与 context logger 中介绍的一样,但是通过这个 logger 对象记录的日志,在日志前面会加上打印该日志的文件路径,以便快速定位日志打印位置。

Service ctx 详解
  • this.ctx.curl 发起网络调用。

  • this.ctx.service.otherService 调用其他 Service。

  • this.ctx.db 发起数据库调用等, db 可能是其他插件提前挂载到 app 上的模块。

注意事项
  • Service 文件必须放在 app/service 目录,可以支持多级目录,访问的时候可以通过目录名级联访问。

app/service/biz/user.js => ctx.service.biz.user // 多级目录,依据目录名级联访问
app/service/sync_user.js => ctx.service.syncUser // 下划线自动转换为自动驼峰
app/service/HackerNews.js => ctx.service.hackerNews // 大写自动转换为驼峰
  • 一个 Service 文件只能包含一个类, 这个类需要通过 module.exports 的方式返回。

  • Service 需要通过 Class 的方式定义,父类必须是 egg.Service。

  • Service 不是单例,是 请求级别 的对象,框架在每次请求中首次访问 ctx.service.xx 时延迟实例化,所以 Service 中可以通过 this.ctx 获取到当前请求的上下文。

使用 Service

// app/controller/user.js
const Controller = require('egg').Controller;
class UserController extends Controller {
  async info() {
    const { ctx } = this;
    const userId = ctx.params.id;
    const userInfo = await ctx.service.user.find(userId);
    ctx.body = userInfo;
  }
}
module.exports = UserController;

// app/service/user.js
const Service = require('egg').Service;
class UserService extends Service {
  // 默认不需要提供构造函数。
  // constructor(ctx) {
  //   super(ctx); 如果需要在构造函数做一些处理,一定要有这句话,才能保证后面 `this.ctx`的使用。
  //   // 就可以直接通过 this.ctx 获取 ctx 了
  //   // 还可以直接通过 this.app 获取 app 了
  // }
  async find(uid) {
    // 假如 我们拿到用户 id 从数据库获取用户详细信息
    const user = await this.ctx.db.query('select * from user where uid = ?', uid);

    // 假定这里还有一些复杂的计算,然后返回需要的信息。
    const picture = await this.getPicture(uid);

    return {
      name: user.user_name,
      age: user.age,
      picture,
    };
  }

  async getPicture(uid) {
    const result = await this.ctx.curl(`http://photoserver/uid=${uid}`, { dataType: 'json' });
    return result.data;
  }
}
module.exports = UserService;

插件

为什么要插件

在使用 Koa 中间件过程中发现了下面一些问题:

  • 中间件加载其实是有先后顺序的,但是中间件自身却无法管理这种顺序,只能交给使用者。这样其实非常不友好,一旦顺序不对,结果可能有天壤之别。

  • 中间件的定位是拦截用户请求,并在它前后做一些事情,例如:鉴权、安全检查、访问日志等等。但实际情况是,有些功能是和请求无关的,例如:定时任务、消息订阅、后台逻辑等等。

  • 有些功能包含非常复杂的初始化逻辑,需要在应用启动的时候完成。这显然也不适合放到中间件中去实现。

中间件、插件、应用的关系

一个插件其实就是一个『迷你的应用』,和应用(app)几乎一样:+

  • 它包含了 Service、中间件、配置、框架扩展等等。

  • 它没有独立的 Router 和 Controller。

  • 它没有 plugin.js,只能声明跟其他插件的依赖,而不能决定其他插件的开启与否。

他们的关系是:

  • 应用可以直接引入 Koa 的中间件。

  • 插件本身可以包含中间件。

  • 多个插件可以包装为一个上层框架。

使用插件

插件一般通过 npm 模块的方式进行复用:

npm i egg-mysql --save

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

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

然后需要在应用或框架的 config/plugin.js 中声明:

// config/plugin.js
// 使用 mysql 插件
exports.mysql = {
  enable: true,
  package: 'egg-mysql',
};

就可以直接使用插件提供的功能:app.mysql.query(sql, values);

egg-mysql 插件文档

参数介绍

plugin.js 中的每个配置项支持:

  • {Boolean} enable - 是否开启此插件,默认为 true

  • {String} package - npm 模块名称,通过 npm 模块形式引入插件

  • {String} path - 插件绝对路径,跟 package 配置互斥

  • {Array} env - 只有在指定运行环境才能开启,会覆盖插件自身 package.json 中的配置

开启和关闭

在上层框架内部内置的插件,应用在使用时就不用配置 package 或者 path,只需要指定 enable 与否:

// 对于内置插件,可以用下面的简洁方式开启或关闭
exports.onerror = false;
根据环境配置

同时,我们还支持 plugin.{env}.js 这种模式,会根据运行环境加载插件配置。

比如定义了一个开发环境使用的插件 egg-dev,只希望在本地环境加载,可以安装到 devDependencies

// npm i egg-dev --save-dev
// package.json
{
  "devDependencies": {
    "egg-dev": "*"
  }
}

然后在 plugin.local.js 中声明:

// config/plugin.local.js
exports.dev = {
  enable: true,
  package: 'egg-dev',
};

这样在生产环境可以 npm i --production 不需要下载 egg-dev 的包了。

注意:

  • 不存在 plugin.default.js

  • 只能在应用层使用,在框架层请勿使用。

package 和 path
  • packagenpm 方式引入,也是最常见的引入方式

  • path 是绝对路径引入,如应用内部抽了一个插件,但还没达到开源发布独立 npm 的阶段,或者是应用自己覆盖了框架的一些插件

// config/plugin.js
const path = require('path');
exports.mysql = {
  enable: true,
  path: path.join(__dirname, '../lib/plugin/egg-mysql'),
};

插件配置

插件一般会包含自己的默认配置,应用开发者可以在 config.default.js 覆盖对应的配置:

// config/config.default.js
exports.mysql = {
  client: {
    host: 'mysql.com',
    port: '3306',
    user: 'test_user',
    password: 'test_password',
    database: 'test',
  },
};

插件列表

框架默认内置了企业级应用常用的插件:

  • onerror 统一异常处理

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值