目录javascript
在 nodejs 发展日益健壮和稳定的状况下,咱们在平常的开发中使用 node 已是一件很是常规的事情了,那么对于咱们必要的掌握一个服务端框架仍是很是有必要的。下面咱们就开始吧。css
在了解目录结构以前,咱们须要对 mvc 的编程模式,进行一个理解。html
Model(模型)
是处理应用程序数据逻辑的部分,主要是和数据库打交道。View(视图)
是做为视图展现使用,若是没有这部分的需求的化, view 这一个层面的内容是能够被省略的。Controller(控制器)
是应用程序中处理与用户交互的部分,一般是为了 视图须要的数据作准备,并向 Model 发送数据(get、post) 等Service(服务)
在实际应用中,Controller 通常不会本身产出数据,也不会包含复杂的逻辑,复杂的过程应抽象为业务逻辑层 Service可是在一些 mvc 框架的论坛中,也有一部分的声音是说但愿,业务层面的架构是一个
Thin Controller Fat Model and no need Service
那么在这样的一个出发点上来将的话,咱们能够在后面实际的业务中去不断尝试。前端
egg-project ├── package.json ├── app.js (可选) ├── agent.js (可选) ├── app │ ├── router.js // 路由设置 │ ├── controller // 控制器 │ └── home.js │ ├── service (可选) // 服务 │ └── user.js │ ├── middleware (可选) // 中间件 │ └── response_time.js │ ├── schedule (可选) // 用于定时任务 │ └── my_task.js │ ├── public (可选) // 静态资源文件 │ └── reset.css │ ├── view (可选) // 模板视图 │ └── home.tpl │ └── extend (可选) // 集成功能小插件 │ ├── helper.js (可选) │ ├── request.js (可选) │ ├── response.js (可选) │ ├── context.js (可选) │ ├── application.js (可选) │ └── agent.js (可选) ├── config // 必要的核心配置 │ ├── plugin.js // 插件配置 │ ├── config.default.js // 默认基础配置 │ ├── config.prod.js // 生产环境 │ ├── config.test.js (可选) // 测试配置 │ ├── config.local.js (可选) // 本地配置 │ └── config.unittest.js (可选) // 待定 └── test // 测试须要 ├── middleware └── response_time.test.js └── controller └── home.test.js
目录结构java
咱们在看 egg 的内置对象的时候,咱们先看下 koa 的内置对象,application、context、request、response。
- 再对比下 egg 的内置对象: 包括从 Koa 继承而来的 4 个对象
(Application, Context, Request, Response)
以及框架扩展的一些对象(Controller, Service, Helper, Config, Logger)
Application 是一个全局对象,上面挂载了这个框架经常使用到的 全局方法和对象。node
Context 是一个请求级别的对象,最多见的 Context 实例获取方式是在 Middleware, Controller 以及 Service 中,例如:git
// controller class HomeController extends Controller { async index() { const { ctx } = this; ctx.body = 'hi, egg, ss'; } }
Request 是一个请求级别的对象,继承自 Koa.Request。封装了 Node.js 原生的 HTTP Request 对象,提供了一系列辅助方法获取 HTTP 请求经常使用参数。github
// app/controller/user.js class UserController extends Controller { async fetch() { const { app, ctx } = this; const id = ctx.request.query.id; ctx.response.body = app.cache.get(id); } } // 可是若是 须要注意的是,获取 POST 的 body 应该使用 ctx.request.body,而不是 ctx.body. 因此咱们在获取 请求对应参数的时候均采用 ctx.request.query 或者 ctx.request.body 的方式.
Response 是一个请求级别的对象,继承自 Koa.Response。封装了 Node.js 原生的 HTTP Response 对象,提供了一系列辅助方法设置 HTTP 响应。数据库
框架提供了一个 Controller 基类,这个基类包括了一些经常使用的属性: ctx、app、config、service、logger 等编程
const Controller = require('egg').Controller; class TestController extends Controller { async index() { const { ctx, app, config, service, logger } = this; ctx.body = 'test'; } } module.exports = TestController;
框架提供了一个 Service 基类, Service 基类的属性和 Controller 基类属性一致,访问方式也相似
const Service = require('egg').Service; class TestService extends Service { index() { const { ctx } = this; return ctx.request.query; } } module.exports = TestService;
Helper 用来提供一些实用的 utility 函数 ( 工具函数 ) ,能够在 Context 的实例上获取到当前请求的 Helper(ctx.helper) 实例。 其中咱们还能够自定义 helper 方法,以下:
// Demo // app/extend/helper.js module.exports = { upperCase(str) { return str.toUpperCase(); } };
咱们能够经过 app.config 从 Application 实例上获取到 config 对象,也能够在 Controller, Service, Helper 的实例上经过 this.config 获取到 config 对象。 被建立出来的原则就是但愿,配置和代码分离的原则。
日志系统包含了 四大层面的 日志对象, 分别是 App Logger、App CoreLogger、Context Logger、Context CoreLogger、Controller Logger & Service Logger
这些从 app、context 、logger、service 等各个层面都提供对应的 debug 方法,这些方法中包括
后需在开发中使用到,再具体介绍每一部分的功能。
经过 config/env 文件指定对应的环境,可是通常推荐使用 构建工具来生成这个文件,那么咱们看下这个文件里面的内容是什么样子的
// config/env prod // 对,就是这么简单。
在 Koa 中咱们经过 app.env 来进行环境判断,app.env 默认的值是 process.env.NODE_ENV。可是在 Egg(和基于 Egg 的框架)中,配置统一都放置在 app.config 上,因此咱们须要经过 app.config.env 来区分环境,app.env 再也不使用。
框架提供了强大且可扩展的配置功能,能够自动合并应用、插件、框架的配置,按顺序覆盖,且能够根据环境维护不一样的配置。合并后的配置可直接从 app.config 获取。
配置文件:
这里简单介绍下,如何编写 中间件 和如何使用 中间件。
从文件夹规则来讲,咱们编写的本身的中间件通常都会放在 app/middleware/ xxx.js 具体的 Demo 以下:
// egg 的中间件 // 基于 洋葱圈模型 // 和 koa 的中间件的构成差很少,只不过 egg 在外面包裹了一层 module.exports = options => { return async function toLowerCase(ctx, next) { await next(); const result = ctx.request.query; ctx.body = result; }; };
// koa 的中间件 function log(ctx) { console.log(ctx.method, ctx.header.host); } module.exports = function() { return async function(ctx, next) { next(); await log(ctx); }; };
在开始问如何使用中间件的时候,咱们要清除一个问题就是咱们每每在何时须要用到中间件、怎么用的问题,下面就简单介绍下。
// config.default.js module.exports = { // 配置须要的中间件,数组顺序即为中间件的加载顺序 middleware: [ 'gzip' ], // 配置 gzip 中间件的配置 gzip: { threshold: 1024, // 小于 1k 的响应体不压缩 }, };
// app.js module.exports = app => { // 在中间件最前面统计请求时间 app.config.coreMiddleware.unshift('report'); }; // app/middleware/report.js module.exports = () => { return async function (ctx, next) { const startTime = Date.now(); await next(); // 上报请求时间 reportTime(Date.now() - startTime); } }; // 核心方法是 app.config.coreMiddleware.unshift('report')
module.exports = app => { const gzip = app.middleware.gzip({ threshold: 1024 }); app.router.get('/needgzip', gzip, app.controller.handler); };
// 修改一些初始化的配置 // config/config.default.js module.exports = { bodyParser: { jsonLimit: '10mb', }, };
module.exports = { bodyParser: { enable: fasle // 不开启使用 bodyParser 中间件 }, gzip: { match: '/static' // 只压缩 /static 目录下的文件 }, apiAgent: { ignore: /^\/api/ // 只用 /api 的请求路径才不会使用 apiAgent 中间件 } };
Router 主要用来描述请求 URL 和具体承担执行动做的 Controller 的对应关系, 框架约定了 app/router.js 文件用于统一全部路由规则
// demo module.exports = app => { const { router, controller } = app; router.get('/', controller.home.index); router.get('/news', controller.news.list); router.get('/user/:id', controller.test.user); };
router 提供的访问方法
router 有哪些使用方式?
主要列举2中方式:
router.verb('path-match', app.controller.action);
router.verb('router-name', 'path-match', middleware1, ..., middlewareN, app.controller.action); // router-name 是指路径别名 // middleware 是指 中间件 // path-match 是指 路由 url
如何使用 RESTful 风格的 URL 定义
// 定义 // router.js router.resources('posts', '/api/posts', controller.posts);
// controller/posts.js exports.index = async ctx => { ctx.body = { success: true, data: [1, 2, 34] }; };
这里须要仔细一点,查看关于 restful 的 使用方法,须要对 restful 有一个基本对理解。
其实整篇文章,写到这里已经初步对于 egg 已经有了一个大概的了解了,那么后续我猜应该还会有对应的实际的写项目的文章吧(但愿有时间和经从来写)
GitHub 地址:(欢迎 star 、欢迎推荐 : )
《前端之路》 - 重温 EggJS