JianghuJS项目结构

目录结构

在JianghuJS中,目录结构沿用 eggjs 的结构规范。

  1. jianghujs-project
  2. ├── package.json
  3. ├── app.js (可选)
  4. ├── agent.js (可选)
  5. ├── app
  6. | ├── router.js
  7. ├── controller
  8. | └── home.js
  9. ├── service (可选)
  10. | └── user.js
  11. ├── middleware (可选)
  12. | └── response_time.js
  13. ├── schedule (可选)
  14. | └── my_task.js
  15. ├── public (可选)
  16. | └── reset.css
  17. ├── view (可选)
  18. | └── page/
  19. └── extend (可选)
  20. ├── helper.js (可选)
  21. ├── request.js (可选)
  22. ├── response.js (可选)
  23. ├── context.js (可选)
  24. ├── application.js (可选)
  25. └── agent.js (可选)
  26. ├── config
  27. | ├── plugin.js
  28. | ├── config.default.js
  29. ├── config.prod.js
  30. | ├── config.test.js (可选)
  31. | ├── config.local.js (可选)
  32. | └── config.unittest.js (可选)
  33. └── test
  34. ├── middleware
  35. | └── response_time.test.js
  36. └── controller
  37. └── home.test.js

如上,由框架约定的目录:

  • app/router.js 用于配置 URL 路由规则,具体参见 Router
  • app/controller/** 用于解析用户的输入,处理后返回相应的结果,具体参见 Controller
  • app/service/** 用于编写业务逻辑层,可选,建议使用,具体参见 Service
  • app/middleware/** 用于编写中间件,可选,具体参见 Middleware
  • app/public/** 用于放置静态资源,可选,具体参见内置插件 egg-static
  • app/extend/** 用于框架的扩展,可选,具体参见框架扩展
  • config/config.{env}.js 用于编写配置文件,具体参见配置
  • config/plugin.js 用于配置需要加载的插件,具体参见插件
  • test/** 用于单元测试,具体参见单元测试
  • app.js 和 agent.js 用于自定义启动时的初始化工作,可选,具体参见启动自定义。关于agent.js的作用参见Agent 机制

由内置插件约定的目录:

  • app/public/** 用于放置静态资源,可选,具体参见内置插件 egg-static
  • app/schedule/** 用于定时任务,可选,具体参见定时任务

【jianghujs核心】中间件-链式调用

中间件本质上是一个函数,它可以对请求进行拦截,处理,以及传递给下一个中间件或者路由处理函数。中间件的使用使得我们可以将请求处理流程进行拆分,将处理逻辑进行模块化,便于代码的复用和维护。

在 Egg.js 中,中间件是通过 appctx 这两个对象进行传递的。在每次请求中,Egg.js 会按照中间件的顺序将 ctx 对象传递给一个个中间件函数,并执行它们的逻辑。在中间件的执行过程中,中间件函数可以根据需要对 ctx 对象进行修改,也可以决定是否将请求传递给下一个中间件或者路由处理函数。这样就可以形成一个链式的请求处理过程,最终得到我们期望的结果。

除了链式调用的核心思想外,Egg.js 中间件还具有以下特点:

  • 可以定义多个中间件,形成一个中间件数组。
  • 中间件可以通过 app 对象进行注册。
  • 中间件的执行顺序可以通过定义中间件的顺序进行控制。
  • 中间件可以通过 ctx 对象进行状态共享,便于数据的传递和处理。
  • 中间件可以根据需要对请求进行拦截、修改和终止等操作。

我们先来通过编写一个简单的 gzip 中间件,来看看中间件的写法。

  1. // app/middleware/gzip.js
  2. const isJSON = require('koa-is-json');
  3. const zlib = require('zlib');
  4. async function gzip(ctx, next) {
  5. await next();
  6. // 后续中间件执行完成后将响应体转换成 gzip
  7. let body = ctx.body;
  8. if (!body) return;
  9. if (isJSON(body)) body = JSON.stringify(body);
  10. // 设置 gzip body,修正响应头
  11. const stream = zlib.createGzip();
  12. stream.end(body);
  13. ctx.body = stream;
  14. ctx.set('Content-Encoding', 'gzip');
  15. }

定时任务

虽然我们通过框架开发的 HTTP Server 是请求响应模型的,但是仍然还会有许多场景需要执行一些定时任务,例如:

  • 定时上报应用状态。
  • 定时从远程接口更新本地缓存。
  • 定时进行文件切割、临时文件删除。

框架提供了一套机制来让定时任务的编写和维护更加优雅。

编写简写模式的定时任务

所有的定时任务都统一存放在 app/schedule 目录下,每一个文件都是一个独立的定时任务,可以配置定时任务的属性和要执行的方法。

一个简单的例子,我们定义一个更新远程数据到内存缓存的定时任务,就可以在 app/schedule 目录下创建一个 update_cache.js 文件。

  1. module.exports = {
  2. schedule: {
  3. interval: '1m', // 1 分钟间隔
  4. type: 'all', // 指定所有的 worker 都需要执行
  5. },
  6. async task(ctx) {
  7. const res = await ctx.curl('http://www.api.com/cache', {
  8. dataType: 'json',
  9. });
  10. ctx.app.cache = res.data;
  11. },
  12. };

这个定时任务会在每一个 Worker 进程上每 1 分钟执行一次,将远程数据请求回来挂载到 app.cache 上。

江湖应用启动的生命周期

在江湖应用程序的启动过程中,以下是应用程序的启动生命周期方法的详细列表(参考项目根目录 app.js ):

  1. configWillLoad:这个方法会在配置文件即将加载时调用,可以在这个方法中加载一些自定义的配置文件。
  2. configDidLoad:这个方法会在配置文件加载完成后调用,可以在这个方法中对加载好的配置文件进行一些操作。
  3. didLoad:这个方法会在应用程序启动时调用,可以在这个方法中进行一些初始化操作。
  4. willReady:这个方法会在应用程序启动时调用,可以在这个方法中加载一些插件或者其他依赖项。
  5. didReady:这个方法会在应用程序启动时调用,可以在这个方法中进行一些初始化完毕后的操作。
  6. serverDidReady:这个方法会在应用程序启动时调用,可以在这个方法中进行一些服务端启动完毕后的操作。
  7. beforeClose:这个方法会在应用程序关闭时调用,可以在这个方法中进行一些清理操作。