13

egg.js是阿里推出的基于koa的node开发框架,今天抽空体验了下,按官方教程做一个Hacker News。其实官方有脚手架提供,但是这次我们不用。

开始之前,我们先看下KOA对于中间件的洋葱模型,了解这个,对于我们之后理解一个请求的执行路径会很有帮助。
68747470733a2f2f7261772e6769746875622e636f6d2f66656e676d6b322f6b6f612d67756964652f6d61737465722f6f6e696f6e2e706e67
好了,让我们开始我们的egg之旅吧!

1, 先初始化项目结构:

$ mkdir egg-example
$ cd egg-example
$ npm init
$ npm i egg --save
$ npm i egg-bin --save-dev

2, 在package.json里,添加如下命令:

"scripts": {
    "dev": "egg-bin dev"
  }

3, 开始编写代码,这之前,我们应该对项目的目录结构有个约定,官方的说明在这里 https://eggjs.org/zh-cn/basic... 我们到时候在回来说下这个目录。

4 我们需要一个controller,一个router,一个config,内如如下

// app/controller/home.js
const Controller = require('egg').Controller;

class HomeController extends Controller {
  async index() {
    this.ctx.body = 'Hello world';
  }
}

module.exports = HomeController;

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


// config/config.default.js
exports.keys = <此处改为你自己的 Cookie 安全字符串>;

此时,目录结构如下:

egg-example
├── app
│   ├── controller
│   │   └── home.js
│   └── router.js
├── config
│   └── config.default.js
└── package.json

好了,现在检查下,运行 npm run dev,
http://127.0.0.1:7001/ 检查下,一切ok

这里有几个地方要注意下

  1. Controller 有class和exports两个写法,那么这两个写法有什么区别呢?

    按照类的方式编写Controller,不仅可以让我们更好的对 Controller层代码进行抽象(例如将一些统一的处理抽象成一些私有方法),还可以通过自定义Controller 基类的方式封装应用中常用的方法。至于方法的方式,官方不推荐使用,只是为了做兼容,其中,每一个 Controller 都是一个 async function,它的入参为请求的上下文 Context 对象的实例,通过它我们可以拿到框架封装好的各种便捷属性和方法 
  2. config也有module.exports 和 exports两种写法,那么这两种写法有什么区别呢?

    module.exports是Module系统创建对象的语法,但是如果你希望你的
    module是一个类的实例,你可以将实例赋予moduel.exports,如这时候
    module.exprots指向的就是一个A的实例了,可以用以下方式调用A的方
    法 module.exports.xxx(),等价于 a.xxx()。但是如果只是exports=
    new ClassA();这个只是简单的将本模块变量exports重新绑定了而已。注
    意,对module.exports的绑定不能是异步绑定或者回调中!
    
    可以这样理解,其他模块使用本模块时,用的是module.exports指向的东
    西,而exports是模块内部变量,外部模块是无法访问的。
    module.exports和exports是两个变量,只是一开始这两个是指向同一个对
    象而已。可以在模块内部用exports作为module.exports的快捷方式
    

现在,我们看下官方的目录结构规范:

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

如上,由框架约定的目录:
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/** 用于定时任务,可选,具体参见定时任务。

若需自定义自己的目录规范,参见 Loader API
app/view/** 用于放置模板文件,可选,由模板插件约定,具体参见模板渲染。
app/model/** 用于放置领域模型,可选,由领域类相关插件约定,如 egg-sequelize。

(未完待续)


座大山
83 声望15 粉丝