Skip to content

中文文档

xiongwilee edited this page Oct 26, 2016 · 5 revisions

注意:该文档为koa-grace v1版本的标准使用文档,v2版本的文档请参阅:https://github.com/xiongwilee/koa-grace/blob/v2.x/README.md#四详细使用手册

1. 简介

koa-grace是基于koa 1.x的新一代Nodejs前后端分离框架。

1.1 特征

为什么koa-grace是新一代Nodejs MVC框架:

  • 一个node服务,多个站点应用;
  • yield 异步语法支持,忘掉回调噩梦;
  • 继承koa中间件,扩展性更强;
  • 支持路径即路由,更优雅的路由方式;
  • RESTful数据代理支持,前后端完全解耦; ……

1.2 目录结构

├── app           // 站点总目录
   ├── blog          // 站点:blog目录
      ├── controller        // 站点:blog的路由(控制器)目录
      ├── model           // 站点:blog的模型目录,包括公共控制器、mongo等
      ├── static          // 站点:blog的静态文件目录
      └── views           // 站点:blog的html模板目录
   ├── reactjs-boilerplate   // 站点:reactjs-boilerplate目录
   └── shop          // 站点:shop目录
├── bin           // server启动器目录
   ├── koa-grace         // TODO:命令行工具
   └── server.js         // server启动器
├── config          // 配置文件目录
   └── main.js         // 主配置文件
├── package.json
└── src           // 核心文件
    └── app.js          // 主文件

2. 快速开始

在开始使用koa-grace之前请确保您已经安装并运行了下面的工具:

  • Nodejs (v4+)

2.1 安装

  $ git clone https://github.com/xiongwilee/koa-grace.git 
  $ cd koa-grace && npm install

2.2 运行

在koa-grace目录下执行:

  $ npm run dev

然后访问 http://127.0.0.1:3000 ,就可以看到koa-grace其中的一个案例站点了!

3. 详细使用文档

虽说koa-grace是一个完整的MVC框架 , 但其本质是基于一种多站点解决方案的koa中间件的集合。其核心中间件包括但不仅限于: koa-router , koa-views , koa-mount , koa-static , koa-grace-vhost , koa-grace-router , koa-grace-proxy , koa-grace-model , koa-grace-mongo , ...

3.1 多站点配置 - Vhost

koa-grace是基于 koa-grace-vhost 进行vhost管理,基本原理是:

一个域名对应一个应用,一个应用对应一个目录

如此一来,配置多站点就很简单了,在配置文件config/main.*.js中:

// vhost配置
vhost: {
  'test.mlsfe.biz':'blog',
  '127.0.0.1':'blog',
  'localhost':'shop',
  '0.0.0.0':'reactjs-boilerplate'
}

其中,vhost配置中127.0.0.1是URI的hostname, blog是站点总目录app下的一个目录app/blog。如果当前请求URI为:http://127.0.0.1:${任意端口}/home 则koa-grace会自动访问app/blog目录里的路由、模型、静态文件。

需要说明的是,多站点配置仅以URI的hostname为主键 ;也就是说,访问带端口号的http://127.0.0.1:3000/home 也会定位到app/blog目录。

3.2 路由及控制器 - Router&Controller

如果你配置好了一个vhost为'127.0.0.1':'blog' , koa-grace就会自动生成一个vhost到app/blog目录了!接下来,进入app/blog/controller目录进行路由配置。

koa-grace基于 koa-grace-router 进行路由管理的,koa-grace-router又是依赖于:koa-router

3.2.1 文件路径即路由

blog站点为例,koa-grace-router会找到 app/blog/* 目录下的所有.js后缀的文件,并以文件路径生成路由。我们再看一下案例中blog的路由文件:

├── api
│   └── post.js
├── dashboard
│   ├── post.js
│   ├── site.js
│   ├── user.js
│   └── userAuthor.js
├── error.js
├── home.js
├── post.js
└── user.js

如果当前请求URI为:http://127.0.0.1/dashboard/post/* 则路由将自动落在dashboard/post.js文件中。

那么,如果请求路径如果是http://127.0.0.1/dashboard/post/list ,这个dashboard/post.js文件是如何控制的呢?

3.2.2 路由文件详细说明

打开app/blog/controller/dashboard/post.js文件:

/*...*/
exports.list = function* () {
  // 绑定默认控制器
  yield this.bindDefault();
  // 独立权限控制
  if (!userAuthor.checkAuth(this, this.userInfo)) {return};
  
  // 获取请求query参数
  let pageNum = this.query.page;
  // 获取数据
  let PostModel = this.mongo('Post');
  let posts = yield PostModel.page(pageNum,20);
  let page = yield PostModel.count(pageNum,20);

  // 渲染模板
  yield this.render('dashboard/post_list',{
    breads : ['文章管理','文章列表'],
    posts:posts,
    page:page,
    userInfo: this.userInfo,
    siteInfo: this.siteInfo
  })
}
exports.list.__method__ = 'get';
exports.list.__regular__ = null;
/*...*/

对,就是你猜的那样:koa-grace-router是通过post.js的module.exports进行下一步的路由控制。

另外,需要说明以下几点:

  • 如果需要配置dashboard/post/list请求为DELETE方法,则post.js中声明 exports.list.__method__ = 'delete'即可(不声明默认会注入get及post请求),更多方法类型请参看:koa-router#routergetputpostpatchdelete--router;
  • 如果要进一步配置dashboard/post/list/id路由,则在post.js中声明exports.list.__regular__ = '/:id';即可,更多相关配置请参看:koa-router#named-routes
  • 如果当前文件路由就是一个独立的控制器,则module.exports返回一个yield方法即可,可以参考案例blog中的controll/home.js
  • 如果当前文件仅仅是一个依赖,仅仅被其他文件引用;则在文件中配置exports.__controller__ = false,该文件就不会生成路由了

当然,如果一个路由文件中的控制器方法都是delete方法,您可以在控制器文件最底部加入:module.exports.__method__ = 'delete'即可。__regular__的配置同理。

3.2.3 控制器

刚刚我们看到了post.js中的exports.list方法,事实上它就是一个控制器(controller)了。

您可以新建一个app/blog/controller/hello.js文件

exports.koagrace = function* (){
  this.body = 'hello koa-grace!';
}

访问 http://127.0.0.1/hello/koagrace ,就可以看到“hello koa-grace!”输出。它是典型的一个基于上下文(context)的yield方法。几个关键方法/参数使用如下:

context属性 类型 说明
this.query object get参数
this.request.body object post参数,由于koa-grace默认引入了koa-bodypaser,您可以直接在this.request.body中获取到post参数
this.bindDefault function 公共控制器,相当于require('app/blog/controller/defaultCtrl.js')
this.cookies.set function 设置cookie,参考:cookies
this.cookies.get function 获取cookie,参考:cookies
this.render function 模板引擎渲染方法,请参看: 模板引擎- Template engine
this.mongo function 数据库操作方法,请参看: 数据库 - Database
this.mongoMap function 并行数据库多操作方法,请参看: 数据库 - Database
this.proxy function RESTful数据请求方法,请参看:数据代理
this.fetch function 从服务器导出文件方法,请参看: 请求代理
this.upload function 文件上传方法,请参看: 文件上传下载
this.download function 文件下载方法,请参看: 文件上传下载

更多context文档请参看koa官网,或http://koajs.in/doc/

3.2.4 控制器中异步函数的写法

方法一、使用Promise

exports.main = function*() {
  let log = yield (function(test) {
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
        resolve(test);
      }, 3000)
    });
  })('测试测试')

  console.log(log);

  yield this.render('module/main');
}

方法二、使用thunkify(需要npm install thunkify 引入thunkify包):

'use strict';

const thunkify = require('thunkify');

exports.index = function*() {
  let log = yield thunkify(function(test, callback) {
    setTimeout(function() {
      callback(null, test);
    }, 3000)
  })('测试');

  console.log(log);

  yield this.render('module/app');
}

3.3 数据库 - Database

注意:koa-grace只是一个前后端分离框架,并不具备强力的数据存储、SESSION管理能职能,该数据库功能仅限于搭建小平台使用

koa-grace引入基于mongoosekoa-grace-mongo ,可以非常便捷地使用mongoDB。

3.3.1 连接数据库

在配置文件config/main.*.js中进行配置:

  // mongo配置
  mongo: {
    options:{
      // mongoose 配置
    },
    api:{
      'blog': 'mongodb://localhost:27017/blog'
    }
  },

其中,mongo.options配置mongo连接池等信息,mongo.api配置站点对应的数据库连接路径。

值得注意的是,配置好数据库之后,一旦koa-grace server启动mongoose就启动连接,直到koa-grace server关闭

3.3.2 mongoose的schema配置

依旧以案例blog为例,参看app/blog/model/mongo目录:

└── mongo
    ├── Category.js
    ├── Link.js
    ├── Post.js
    └── User.js

一个js文件即一个数据库表即相关配置,以app/blog/model/mongo/Category.js

'use strict';

// model名称,即表名
let model = 'Category';

// 表结构
let schema = [{
  id: {type: String,unique: true,required: true},
  name: {type: String,required: true},
  numb: {type: Number,'default':0}
}, {
  autoIndex: true,
  versionKey: false
}];

// 静态方法:http://mongoosejs.com/docs/guide.html#statics
let statics = {}

// 方法扩展 http://mongoosejs.com/docs/guide.html#methods
let methods = {
  /**
   * 获取博客分类列表
   */
  list: function* () {
    return this.model('Category').find();
  }
}

module.exports.model = model;
module.exports.schema = schema;
module.exports.statics = statics;
module.exports.methods = methods;

主要有四个参数:

  • model , 即表名,最好与当前文件同名
  • schema , 即mongoose schema
  • methods , 即schema扩展方法,推荐把数据库元操作都定义在这个对象中
  • statics , 即静态操作方法

3.3.3 在控制器中调用数据库

在控制器中使用非常简单,主要通过this.mongo,this.mongoMap两个方法。

1) this.mongo(name)

调用mongoose Entity对象进行数据库CURD操作

参数说明:

@param [string] name : 在app/blog/model/mongo中配置Schema名,

返回:

@return [object] 一个实例化Schema之后的Mongoose Entity对象,可以通过调用该对象的methods进行数据库操作

案例

参考上文中的Category.js的配置,以app/blog/controller/dashboard/post.js为例,如果要在博客列表页中获取博客分类数据:

// http://127.0.0.1/dashboard/post/list
exports.list = function* (){
  let cates = yield this.mongo('Category').list();
  this.body = cates;
}

2)this.mongoMap(option)

并行多个数据库操作

参数说明

@param [array] option

@param [Object] option[].model mongoose Entity对象,通过this.mongo(model)获取

@param [function] option[].fun mongoose Entity对象方法

@param [array] option[].arg mongoose Entity对象方法参数

返回

@return [array] 数据库操作结果,以对应数组的形式返回

案例

  let PostModel = this.mongo('Post');
  let mongoResult = yield this.mongoMap([{
      model: PostModel,
      fun: PostModel.page,
      arg: [pageNum]
    },{
      model: PostModel,
      fun:PostModel.count,
      arg: [pageNum]
    }]);

  let posts = mongoResult[0];// 获取第一个查询PostModel.page的结果
  let page = mongoResult[1]; // 获取第二个查询PostModel.count的结果,两者并发执行

3.4 代理 - Proxy

除了在控制器中直接进行数据库操作,Web应用还有可能由其他服务进行后端部署。针对这种场景,koa-grace引入了基于 Requestkoa-grace-proxy

3.4.1 数据代理

this.proxy(object|string,[destObj])

在koa-grace的控制器中使用this.proxy方法进行数据代理非常简单:

exports.list = function* (){
  yield this.proxy({
    userInfo:'github:post:user/login/oauth/access_token?client_id=****',
    otherInfo:'github:other/info?test=test',
  });
  
  console.log(this.backData);
  /**
   *  {
   *    userInfo : {...},
   *    otherInfo : {...}
   *  }
   */
}

你也可以不传this.backData参数,默认注入到上下文的this.backData对象中:

exports.list = function* (){
  yield this.proxy({
    userInfo:'github:post:user/login/oauth/access_token?client_id=****',
    otherInfo:'github:other/info?test=test',
  });
  
  console.log(this.backData);
  /**
   *  {
   *    userInfo : {...},
   *    otherInfo : {...}
   *  }
   */
}

另外,github:post:user/login/oauth/access_token?client_id=****说明如下:

  • github: 为在config.main.jsapi 对象中进行配置;
  • post : 为数据代理请求的请求方法,该参数可以不传,默认为get
  • path: 后面请求路径中的query参数会覆盖当前页面的请求参数(this.query),将query一同传到请求的接口
  • 你也可以写完整的路径:{userInfo:'https://api.github.com/user/login?test=test'}

3.4.2 文件代理

this.fetch(string)

文件请求代理也很简单,比如如果需要从github代理一个图片请求返回到浏览器中,参考:http://mlsfe.biz/user/avatar?img=https://avatars.githubusercontent.com/u/1962352?v=3 , 或者要使用导出文件的功能:

exports.avatar = function* (){
  let imgUrl = query.img;
    yield this.fetch(imgUrl);
}

3.5 文件上传下载 - Xload

注意:同数据库功能,koa-grace只是前后端分离框架,不具备任何数据库、SESSION功能;文件上传下载建议由后端服务来完成

3.5.1 文件上传

方法:

this.upload([opt])

示例:

exports.aj_upload = function*() {
  yield this.bindDefault();

  let files = yield this.upload();
  let res = {};

  if (!files || files.length < 1) {
    res.code = 1;
    res.message = '上传文件失败!';
    return this.body = res; 
  }

  res.code = 0;
  res.message = '';
  res.data = {
    files: files
  }

  return this.body = res;
}
exports.aj_upload.__method__ = 'post';

3.5.2 文件下载

方法:

this.download(filename, [opt])

示例:

exports.download = function*() {
  yield this.bindDefault();

  let file = this.query.file;

  yield this.download(file);
}

3.6 模板引擎- Template engine

koa-grace引入koa-views , 进行模板引擎管理。默认的模板引擎为swig, 您可以在config/main.js中配置template属性您想要模板引擎:

  // 模板引擎配置
  template: 'swig'

你还可以根据不同的模块配置不同的模板引擎:

  // 模板引擎配置
  template: {
		wallet:'etc'
		// 其他模块默认使用 swig
	}

目前支持的模板引擎列表在这里:consolidate.js#supported-template-engines

在控制器中调用this.render方法渲染模板引擎:

exports.home = function* () {
  yield this.bindDefault();

  yield this.render('dashboard/site_home',{
    breads : ['站点管理','通用'],
    userInfo: this.userInfo,
    siteInfo: this.siteInfo
  })
}

模板文件在app/blog/views目录中。

3.7 静态文件服务 - Static server

建议: 在生产环境中使用nginx作为静态文件服务

koa-grace引入koa-grace-static,将静态文件代理到/static

// 配置静态文件路由
app.use(_static(['/static/**/*', '/*/static/**/*'], {
  dir: config_path_project,
  maxage: config_site.env == 'production' && 60 * 60 * 1000
}));

以案例中blog的静态文件为例,静态文件在blog项目下的路径为:app/blog/static/image/bg.jpg,则访问路径为http://127.0.0.1/blog/static/image/bg.jpg 或者 http://127.0.0.1/static/blog/image/bg.jpg

3.8 Mock数据 - Mock

在koa-grace模块的开发环境中你可以很轻易地使用MOCK数据。

依旧以blog模块为例,首先在main.js配置文件中添加proxy配置:

  // controller中请求各类数据前缀和域名的键值对
  api: {
		 // ...
    blog: 'http://${ip}:${port}/__MOCK__/blog/'
		 // ...
  },

然后,在blog模块中添加mock文件夹,然后添加test.json:

文件结构:

.
├── controller
├── deploy
├── mock
├── model
|          └── test.json
├── static
└── views

文件内容(就是你想要的请求返回内容):

{
	code:0
}

然后,你可以打开:http://${ip}:${port}/MOCK/blog/test 验证是否已经返回了test.json里的数据。

最后在你的controller业务代码中就可以通过proxy方法获取mock数据了:

this.proxy({
	test:'blog:test'
})

注意:

  • 如果你的mock文件路径是/mock/test/sub_test.json 那么proxy路径则是:test/sub_test;
  • 强烈建议将mock文件统一为真正的后端请求路径,这样以实现真实路径的mock

3.9 启动服务及调试 - Process & DEBUG

3.9.1 开发环境

在开发环境可以使用npm命令完成。

  1. 普通启动:

$ npm run start

  1. watch启动:

$ npm run dev

在80端口启动

$ npm PORT=80 run dev

DEBUG模式启动,默认为DEBUG=koa-grace*

$ npm DEBUG=* run dev

3.9.2 生产环境

在生产环境推荐使用 pm2 进行进程管理:

$ npm install pm2 -g $ pm2 start node ./bin/server.js

更多使用方法,请参看 pm2

4. 贡献