中文文档

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. 贡献

Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.