知而获智,智达高远

趣店前端团队基于koajs的前后端分离实践

趣店前端团队基于koajs的前后端分离实践

前言

关于前后端分离,我的感觉其实也是:这么老土的话题,为什么还要拿出来老调重弹?

但越来越发现基于前后端分离的类RESTful架构,能很好的满足WebAPP的业务需求。尤其是WebAPP+NativeAPP产品为主的中小型公司,能让整个公司的服务端研发和部署更灵活。

PS:已经了解前后端分离和koajs(不喜欢看背景和扯淡)同学可以直接跳到:三、如何实践。

一、什么是前后端分离?

前后端分离的概念和优势在这里不再赘述,有兴趣的同学可以看各个前辈们一系列总结和讨论:

尤其是《前后端分离的思考与实践》系列文章非常全面的阐述了前后端分离的意义和技术实现。如果不想看上面的文章,可以在脑海里留下这样一个轮廓就好:

前后端分离

本文主要阐述趣店团队基于Koajs的前后端分离实践方案。

二、为什么选择koa?

koa是由Express 原班人马打造的一个更小、更健壮、更富有表现力的 Web 框架。(koa官网需用梯子,中文文档可以参考郭宇大神的翻译:koa中文文档,这里也不详细介绍。)

koa

看到koa之后,简单看了下文档和源码,立刻感觉koa不就是为前后端分离而诞生的轻量级框架吗?

因为:

  • 如果要用koa实现一整套类似于PHP的Laravel或者Ruby on Rails的服务端MVC框架其实还有很长的路要走;
  • koa中间件的思路可以很好的被前后端分离的思路利用起来,比如:路由、模板引擎、数据代理等等;
  • koa基于ES6的generator特性(不了解generator的同学可以参考阮一峰老师的文章),利用co等模块让前端同学写一个路由再简单不过;
  • ……

总之,koa给我的第一印象就是:如果用它来实现一套前后端分离的框架,非常高效轻量易扩展

三、如何实践?

1、刀耕火种的年代

在谈如何实现基于koajs的前后端分离框架的之前,必须提一下在做分离之前趣店(原趣分期)的情况:

  • 一个仓储托管了趣分期业务前后端所有代码,由于不断维护,这个仓储足足有600M
  • 后端为了满足NativeAPP的接口需求,这个600M的仓储还包含了提供给客户端的代码
  • 前后端开发流程为:先由后端写一个路由 → 前端将这个路由写成可以交互的页面 → 前端提交代码后后端同学“套模板”填充数据
  • 前后端开发都在一台远程开发机上,通过samba隐射到本地进行开发;
  • 前端几乎没有做任何打包编译;有时候为了偷懒,甚至把CSS和JS直接以内嵌的方式写在模板里(不要去联想vue,两码事儿)

写到这儿想贴代码来着,想想还是算了……

2、基于koa的实现

无论使用什么工具要实现前后端分离框架,无非要满足这样几点:

  • 更便捷地创建路由
  • 更高效地代理数据请求
  • 更灵活地环境部署

先看一个已经实现了demo应用的目录结构:

1
.
└── demo
    ├── controller
    │   ├── data.js
    │   ├── defaultCtrl.js
    │   └── home.js
    ├── static
    │   ├── css
    │   ├── image
    │   └── js
    └── views
        └── home.html

这个结构大家再熟悉不过:controller提供路由;static包含静态文件;views提供模板文件。
这里可能大家会有疑问,如果我的前端构建是components的模式怎么办呢?

1
├──components_a
│   ├── home.js
│   ├── home.css
│   └── home.html
├──components_b
...

没关系,do what u want!很多boilerplate可以直接拿到这个框架下直接使用,我们团队自己也正在使用了requirejs和vue的构建方案。

1)更便捷地创建路由:controller

回到上文提到的案例:

1
controller
├── data.js
└── home.js

这段代码生成的是一个/data/*,/home/*路由,用过Express的同学对这种路由实现肯定一点都不陌生。

但有一点不一样的是,koa下面每一个路由是一个独立的generator函数(很遗憾,generator函数不能使用箭头函数的语法来表示),参看controller/home.js

1
exports.index = function* () {
  yield this.bindDefault();
  yield this.render('home', {
    title: 'Hello , Grace!'
  });
}

你还可以用一个对象把一个controller包起来,,参看controller/data.js

1
exports.info = {
  repo: function*(){
    yield this.proxy('github:repos/xiongwilee/koa-grace')
  }
}

这种写法非常适用于仅仅用来做一个数据代理的ajax请求的控制器。当然,如果需要的话,你还可以在控制器里对数据做一些加工。

另外,还有几个细节与express的路由控制器不太一样:

  1. 终止请求的动作都有koa来完成,在这里的controller里,你不需要通过res.end()来终止请求;
  2. 这里的controller的作用域,就是koa的上下文,所以你可以通过this.req获取request对象;
  3. 这里的controller里,异步回调的语法成为了往事,请尽情的使用yield

我们对现在的路由方式做了一个压测:8核CPU/8G内存的机器,通过一个路由返回hello world!,结果显示:8核CPU/8G至少能扛住1000QPS的压力(cpu idle不到60%,cpu load接近8)。

2)更高效地代理数据请求:proxy

前后端分离模式下的,数据代理主要有这两种使命:

  • 一种是,拼装一个和多个数据结果,给模板渲染数据或者给Ajax接口
  • 另外一种是,接受用户的数据请求,将结果交给一个后端接口

第一种场景,比如:一个用户中心的页面,需要展示用户的用户信息、订单列表、喜欢的商品推荐等等信息。由于后端的架构,用户、订单、商品可能都是属于不同的服务,这就意味着,后端同学需要给你同时拼装这么多数据,拼装还有可能是同步获取每个接口然后再一起返回给前端。

现在,就可以尽情的发挥nodejs异步并发及koa的generator同步语法的优势,我们还可以这么写:

1
repo: function*(){
  yield this.proxy({
   data1:'github:repos/xiongwilee/data1',
   data2:'github:repos/xiongwilee/data2',
   data3:'github:repos/xiongwilee/data3'
  })
}

koa会自动并发请求所有接口,然后将结果返回给你,然后可以在上下文的backData中获取结果集。

另外一种场景,比如用户需要上传一张图片或者提交一篇文章,你也可以这样写:

1
submit: function*(){
  yield this.proxy('http://test.com/submit')
}

不用做任何配置,koa会直接把request数据buffer直接通过管道pipe给后端接口。

值得一提的是:

  • 有没有发现,这种proxy方式是没有同域机制的限制 了,前端面试常问的跨域方案都不是事儿;
  • 另外,同学们可能觉得,本来一个请求都能搞定的事情非要用nodejs代理一次,会不会很慢?事实上,我们的测试结果显示:内网访问的情况下,nodejs代理耗时不超过10ms

最后,我们也做了一个压测:8核CPU/8G内存的机器,通过一个路由将请求代理到另外一个接口,结果显示:8核CPU/8G至少能扛住300QPS的压力,这个结果比纯路由的压测情况要低很多,值得注意。

不过有意思的一点是,我们压测发现:proxy的性能与接口响应时间无关,与接口响应content大小有密切关系:接口响应内容越大,proxy性能越差

3) 更灵活地环境部署

基于koa可以实现各类有意思的中间件。比如:可以拦截请求计算请求总耗时中间件、可以匹配路径实现本地的静态文件服务器、可以不需要再另开server实现一个mock数据的功能……

a) 开发环境

上文提到过一个DEMO的文件目录,具体在开发环境和生产环境中整体目录结构中其实是这样的:

1
├── app				// 开发模式下应用模块目录
│   ├── blog
│   └── demo
├── cli				// 配套命令行工具
│   ├── bin
│   ├── lib
│   └── package.json
├── log				// 日志目录
└── server			// 服务器目录
	├── app			// 实际服务端引用的模块目录
	│   ├── blog
	│   └── demo
    ├── bin
    ├── config
    ├── src
    └── package.json

在开发环境下,你可以将./app下的项目源文件直接编译到./server/app目录,这就意味着:./app/目录下的各个项目,你自己想怎么玩就怎么玩

我们目前有基于gulp+requirejs的模块化方案和基于webpack+vue的前端构建方案,其中gulp+requirejs的方案已经开源:https://github.com/xiongwilee/gulp-requirejs-boilerplate

基于上文提到的路由和数据代理的功能,现在我们的开发模式就可以优化为:前后端确定接口→前后端同时开发→联调提测上线。给开发带来的好处自不必说:

  • 后端接口完全解耦,前端利用异步并发,性能可以达到最优;
  • WebAPP和NativeAPP可以通用一套接口,后端省了不少成本;
  • 前后端独立之后,前端构建的成本更低,灵活性更高;
  • ……
b) 生产环境

生产环境的部署与大多数服务部署类似:经过SLB之后到nginx进行反向代理,走到nodejs服务响应请求;可以在nginx层进行负载均衡和监控;如果需要用到缓存,可以在nginx和nodejs之间再加一层Varnish

这里必须要推荐下最牛哔的nodejs进程管理工具:pm2,完成了趣店生产环境下nodejs进程管理和nodejs日志切分。

然后提一下,我们的沙盒环境和测试环境部署。

沙盒只需要摘一台性能比较low的线上机器,提供给办公网络访问就行。因为数据代理的接口直接走的服务端对应环境的接口,整个沙盒环境和测试环境的搭建也非常愉快。

另外,我们对线上业务做了一个压测:单台8核CPU/8G机器,能扛住500QPS左右的压力

最后,关于环境部署,再提两个场景:

  • 一个场景是,超越前后端分离框架的本职工作,实现团队文档系统

    因为前后端分离框架本身是不支持数据库和SESSION存储功能的,为了满足一些简单的数据库和文件上传需求,我们自己实现了两个中间件,一个能够利用mongoose很简单地与mongo打交道,一个利用formidable,koa-send模块轻易实现文件上传和下载;从而很快实现了团队博客和文档系统。

    当然了,这个文档系统和团队博客仅限在内网系统中使用。

  • 另外一场景是,后端环境使用Java MVC框架下的前后端独立开发

    我们团队与蚂蚁金服团队深度合作(事实上是作为TP,实现支付宝下的业务需求),必须使用蚂蚁金服的基于Java的sofalite MVC框架,而且坑爹的是:蚂蚁金服不能提供他们的前端构建框架!

    这就意味着:我们团队的前端同学需要回到刀耕火种时代,跟后端同学用一个仓储自己搭建Java环境进行本地开发了。

    但是,别忘了,我们现在有基于koa的前后端分离框架啊(知乎腔)!

    事实上,前后端独立开发的唯一瓶颈就在与Java的velocity模板引擎;而我们的前后端分离框架的模板引擎是可配的。

    所以,前后端定好了接口,然后前端就愉快地用上mock数据独立开发去了;最后,整个项目提前四天开发联调完成

四、koa-grace

主(guang)角(gao)终于要出场了!

)

这套前后端分离框架——koa-grace:基于koa的前后端分离框架已经开源。欢迎各路大神star/fork/提ISSUE,谢谢!!!

另外,配套命令行工具暂时仅限内网使用,后续会开源出来。最后,关于koa-garce还得啰嗦两点:

1)koa-grace是基于koa 1.x版本

目前koa-grace所有的中间件都是基于koa 1.x,而koa 2.x正式发布之后会立刻跟进。

但koa-grace毕竟只是一个轻量级的框架:Requiring babel to run your server is just not good developer experience.(引自:koa/issues/533)。

2)koa-grace需要完善测试用例

vue的作者尤雨溪说过,不写测试的开源项目不是合格的开源项目。而koa-grace目前还没有测试用例覆盖,希望大家一起来完善。

最后

无论前端怎么玩、无论技术怎么发展,任何架构最终都是为了服务于产品

减少技术成本,提升迭代效率和产品体验是Web技术的核心诉求(尤其是在做产品不快则死的创业公司)。

趣店产品项目在逐渐升级,我们也在随着产品升级而将业务迁移到这套前后端分离架构上来;目前来看,koa-grace给趣店产品和技术部署带来了不少便利。

就说到这儿,老板喊我去写PHP了…………

koa-grace

koa-grace诞生记:19天打造nodejs多应用MVC框架及基于该框架的CMS博客系统

koa-grace中文文档

1. koa-grace简介

koa-grace是基于koa 1.x的新一代Nodejs多站点MVC框架。项目地址:https://github.com/xiongwilee/koa-grace

1.1 特征

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

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

1.2 目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
├── 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+)
  • MongoDB (DEMO 演示使用,正式环境可以配置不用)

2.1 安装

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

2.2 配置

在koa-grace目录下,打开配置文件:

$ vi config/main.js

修改配置项:config.mongo.blog 为您的本地mongoDB的路径:

// mongo configuration
mongo: {
  'blog': 'mongodb://localhost:27017/blog'
}

**更多关于配置项的文档可以看下面的使用文档。

2.3 运行

在koa-grace目录下执行,可能需要root权限:

$ npm run dev

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

您也可以访问koa-grace的一个线上应用: http://mlsfe.biz

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中:

1
2
3
4
5
6
7
// 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的路由文件:

1
├── 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文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*...*/
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请求为POST方法,则post.js中声明 exports.list.__method__ = 'post'即可(不声明默认为get请求),更多方法类型请参看: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,该文件就不会生成路由了

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

3.2.3 控制器

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

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

1
2
3
exports.koagrace = funtion* (){
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/model/defaultCtrl.js')
this.render function 模板引擎渲染方法,请参看:3.5 模板引擎- Template engine
this.mongo function 数据库操作方法,请参看:3.3 数据库 - Database
this.mongoMap function 并行数据库多操作方法,请参看:3.3 数据库 - Database
this.proxy function RESTful数据请求方法,请参看:3.4.1 数据代理
this.download function 文件请求代理方法,请参看:3.4.2 请求代理
this.upload function 文件上传方法,请参看: 3.4.3 文件上传

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

3.3 数据库 - Database

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

3.3.1 连接数据库

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

1
2
3
4
5
6
7
8
9
// 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目录:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
'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为例,如果要在博客列表页中获取博客分类数据:

1
2
3
4
5
// 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] 数据库操作结果,以对应数组的形式返回

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
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 数据代理

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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对象中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 文件代理

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

1
2
3
4
exports.avatar = function* (){
let imgUrl = query.img;
yield this.download(imgUrl);
}

3.4.3 文件上传

TODO: 文件上传并代理到其他服务或者存储到本地

3.5 模板引擎- Template engine

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

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

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

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

1
2
3
4
5
6
7
8
9
exports.home = function* () {
yield this.bindDefault();

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

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

3.6 静态文件服务 - Static server

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

1
2
3
4
// 配置静态文件路由
vapp.use(mount('/static',
koastatic(appPath + '/static')
));

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

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

3.7.1 开发环境

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

1) 普通启动:

$ npm run start

2) watch启动:

$ npm run dev
# 在80端口启动
$ npm PORT=80 run dev
# DEBUG模式启动,默认为DEBUG=koa-grace*
$ npm DEBUG=* run dev 

3.7.2 生产环境

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

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

更多使用方法,请参看 pm2

4. 贡献

web前端之原生DOM&jQuery-DOM编程习题【清华大学】

清华大学web前端之原生DOM&jQuery-DOM编程习题

web前端之原生DOM&jQuery-DOM编程讲稿【清华大学】

清华大学web前端之原生DOM&jQuery-DOM编程讲稿