Node.JS開源:koa-grace - 基于koa的標準前后端分離框架
koa-grace v1.x版本請移步: https://github.com/xiongwilee/koa-grace/tree/master
koa-grace v2
Gracejs (又稱:koa-grace v2) 是全新的基于 koa v2.x 的MVC+RESTful架構的前后端分離框架。
一、簡介
Gracejs是 koa-grace 的升級版,也可以叫koa-grace v2。
主要特性包括:
- 支持MVC架構,可以更便捷地生成服務端路由;
- 標準的RESTful架構,支持后端接口異步并發,頁面性能更優;
- 一套Node環境經服務服務多個站點應用,部署更簡單;
- 優雅的MOCK功能,開發環境模擬數據更流暢;
- 完美支持async/await及generator語法,隨心所欲;
- 更靈活的前端構建選型,想用什么就用什么,隨你所愿。
相比于koa-grace v1(以下簡稱:koa-grace): Gracejs完美支持koa v2 ,同時做了優化虛擬host匹配和路由匹配的性能、還完善了部分測試用例等諸多升級。當然,如果你正在使用koa-grace也不用擔心,我們會把Gracejs中除了支持koa2的性能和功能特性移植到koa-grace的相應中間件中。
二、快速開始
注意:請確保你的運行環境中Nodejs的版本至少是 v4.0.0 ,目前需要依賴Babel。 (當然26日凌晨 nodejs v7 已經release,你也可以不依賴Babel,直接通過 --harmony_async_await 模式啟動。)
安裝
執行命令:
$ git clone -b v2.x https://github.com/xiongwilee/koa-grace.git
$ cd koa-grace && npm install
運行
然后,執行命令:
$ npm run dev
然后訪問: http://127.0.0.1:3000 就可以看到示例了!
三、案例說明
這里參考 https://github.com/xiongwilee/koa-grace/tree/v2.x 中 app/demo 目錄下的示例,詳解Gracejs的MVC+RESTful架構的實現。
此前也有文章簡單介紹過koa-grace的實現( https://github.com/xiongwilee/koa-grace/wiki ),但考慮到Gracejs的差異性,這里再從 目錄結構 、 MVC模型實現 、 proxy機制 這三個關鍵點做一些比較詳細的說明。
目錄結構
Gracejs與koa-grace v1.x版本的目錄結構完全一致:
.
├── controller
│ ├── data.js
│ ├── defaultCtrl.js
│ └── home.js
├── static
│ ├── css
│ ├── image
│ └── js
└── views
└── home.html
其中:
- controller 用以存放路由及控制器文件
- static 用以存放靜態文件
- views 用以存放模板文件
需要強調的是, 這個目錄結構是生產環境代碼的標準目錄結構。在開發環境里你可以任意調整你的目錄結構,只要保證編譯之后的產出文件以這個路徑輸出即可 。
MVC模型實現
為了滿足更多的使用場景,在Gracejs中加入了簡單的Mongo數據庫的功能。
但準確的說,前后端的分離的Nodejs框架都是VC架構,并沒有Model層。因為 前后端分離框架不應該有任何數據庫、SESSION存儲的職能 。
如上圖,具體流程如下:
- 第一步,Nodejs server(也就是Gracejs服務)監聽到用戶請求;
- 第二步,Gracejs的各個中間件(Middlewares)對請求上下文進行處理;
- 第三步,根據當前請求的path和method,進入對應的Controller;
- 第四步,通過http請求以proxy的模式向后端獲取數據;
- 第五步,拼接數據,渲染模板。
這里的第四步,proxy機制,就是Gracejs實現前后端分離的核心部分。
proxy機制
以實現一個電商應用下的“個人中心”頁面為例。假設這個頁面的首屏包括:用戶基本信息模塊、商品及訂單模塊、消息通知模塊。
后端完成服務化架構之后,這三個模塊可以解耦,拆分成三個HTTP API接口。這時候就可以通過Gracejs的 this.proxy 方法,去后端異步并發獲取三個接口的數據。
如下圖:
這樣有幾個好處:
- 在Nodejs層(服務端)異步并發向后端(服務端)獲取數據,可以使HTTP走內網,性能更優;
- 后端的接口可以同時提供給客戶端,實現接口給Web+APP復用,后端開發成本更低;
- 在Nodejs層獲取數據后,直接交給頁面,不管前端用什么技術棧,可以使首屏體驗更佳。
那么,這么做是不是就完美了呢?肯定不是:
- 后端接口在外網開放之后,如何保證接口安全性?
- 如果當前頁面請求是GET方法,但我想POST到后端怎么辦?
- 我想在Controller層重置post參數怎么辦?
- 后端接口設置cookie如何帶給瀏覽器?
- 經過一層Nodejs的代理之后,如何保證SESSION狀態不丟失?
- 如果當前請求是一個file文件流,又該怎么辦呢? ...
好消息是,這些問題在proxy中間件中都考慮過了。這里不再一一講解,有興趣可以看koa-grace-proxy的源碼: https://github.com/xiongwilee/koa-grace/tree/v2.x/middleware/proxy 。
四、詳細使用手冊
在看詳細使用手冊之前,建議先看一下Gracejs的主文件源碼: https://github.com/xiongwilee/koa-grace/blob/v2.x/src/app.js 。
這里不再浪費篇幅貼代碼了,其實想說明的就是: Gracejs是一個個關鍵中間件的集合 。
所有中間件都在 middleware 目錄下,配置由 config/main.*.js 管理。
關于配置文件:
- 配置文件extend關系為:config/server.json的merge字段 > config/main.*.js > config.js;
- 配置生成后保存在Gracejs下的全局作用域 global.config 里,方便讀取。
下面介紹幾個關鍵中間件的作用和使用方法。
vhost——多站點配置
vhost 在這里可以理解為,一個Gracejs server服務于幾個站點。Gracejs支持通過 host 及 host + 一級path 兩種方式的 映射 。所謂的隱射,其實就是一個域名(或者一個域名+一級path)對應一個應用,一個應用對應一個目錄。
注意:考慮到正則的性能問題,vhost不會考慮正則映射。
參考 config/main.development.js ,可以這么配置vhost:
// vhost配置
vhost: {
'127.0.0.1':'demo',
'127.0.0.1/test':'demo_test',
'localhost':'blog',
}
其中, demo , demo_test , blog 分別對應 app/ 下的三個目錄。當然你也可以指定目錄路徑,在配置文件中修改 path.project 配置即可:
// 路徑相關的配置
path: {
// project
project: './app/'
}
router——路由及控制器
Gracejs中生成路由的方法非常簡單,以自帶的demo模塊為例,進入demo模塊的controller目錄: app/demo/controller 。
文件目錄如下:
controller
├── data.js
├── defaultCtrl.js
└── home.js
1、 文件路徑即路由
router中間件會找到模塊中所有以 .js 結尾的文件,根據文件路徑和module.exports生成路由。
例如,demo模塊中的home.js文件:
exports.index = async function () {
await this.bindDefault();
await this.render('home', {
title: 'Hello , Grace!'
});
}
exports.hello = function(){
this.body = 'hello world!'
}
則生成 /home/index 、 /home 、 /home/hello 的路由。需要說明幾點:
- 如果路由是以 /index 結尾的話,Gracejs會"贈送"一個去掉 /index 的同樣路由;
- 如果當前文件是一個依賴,僅僅被其他文件引用;則在文件中配置 exports.__controller__ = false ,該文件就不會生成路由了;參考 defaultCtrl.js
- 這里的控制器函數可以是 await/async 或 generator 函數,也可以是一個普通的函數;Gracejs中推薦使用 await/async ;
- 這里的路由文件包裹在一個目錄里也是可以的,可以參考: app/blog 中的controller文件;
- 如果當前文件路由就是一個獨立的控制器,則 module.exports 返回一個任意函數即可。
最后,如果用戶訪問的路由查找不到,router會默認查找 /error/404 路由,如果有則渲染 error/404 頁(不會重定向到 error/404 ),如果沒有則返回404。
2、 路由文件使用說明
將demo模塊中的home.js擴展一下:
exports.index = async function () {
...
}
exports.index.__method__ = 'get';
exports.index.__regular__ = null;
另外,需要說明以下幾點:
- 如果需要配置dashboard/post/list請求為 DELETE 方法,則post.js中聲明 exports.list.__method__ = 'delete' 即可( 不聲明默認注入get及post方法 );
- 如果要配置更靈活的路由,則中聲明 exports.list.__regular__ = '/:id'; 即可
- 需要注意的是:如果 __regular__ 配置為正則表達式的話,則會生成當前控制器默認路由及正則可匹配的路由
當然,如果路由文件中的所有控制器方法都是post方法,您可以在控制器文件最底部加入: module.exports.__method__ = 'post' 即可, __regular__ 的配置同理。
注意:一般情況這里不需要額外的配置,為了保證代碼美觀,沒有特殊使用場景的話就不要寫 __method__ 和 __regular__ 配置。
3、 控制器
將demo模塊中的home.js的index方法再擴展一下:
exports.index = async function () {
// 綁定默認控制器方法
await this.bindDefault();
// 獲取數據
await this.proxy(...)
// 渲染目標引擎
await this.render('home', {
title: 'Hello , Grace!'
});
}
它就是一個標準的控制器(controller)了。這個控制器的作用域就是當前koa的context,你可以任意使用koa的context的任意方法。
幾個關鍵context屬性的使用說明如下:
koa自帶:
更多koa自帶context屬性,請查看koajs官網: http://koajs.com/
context屬性 | 類型 | 說明 |
---|---|---|
this.request.href | String | 當前頁面完整URL,也可以簡寫為 this.href |
this.request.query | object | get參數,也可以簡寫為 this.query |
this.response.set | function | 設置response頭信息,也可以簡寫為 this.set |
this.cookies.set | function | 設置cookie,參考: cookies |
this.cookies.get | function | 獲取cookie,參考: cookies |
Gracejs注入:
context屬性 | 類型 | 中間件 | 說明 |
---|---|---|---|
this.bindDefault | function | router | 公共控制器,相當于 require('app/*/controller/defaultCtrl.js') |
this.request.body | object | body | post參數,可以直接在this.request.body中獲取到post參數 |
this.render | function | views | 模板引擎渲染方法,請參看: 模板引擎- Template engine |
this.mongo | function | mongo | 數據庫操作方法,請參看: 數據庫 - Database |
this.mongoMap | function | mongo | 并行數據庫多操作方法,請參看: 數據庫 - Database |
this.proxy | function | proxy | RESTful數據請求方法,請參看:數據代理 |
this.fetch | function | proxy | 從服務器導出文件方法,請參看: 請求代理 |
this.backData | Object | proxy | 默認以Obejct格式存儲this.proxy后端返回的JSON數據 |
this.upload | function | xload | 文件上傳方法,請參看: 文件上傳下載 |
this.download | function | xload | 文件下載方法,請參看: 文件上傳下載 |
4、控制器中異步函數的寫法
在控制器中,如果還有其他的異步方法,可以通過Promise來實現。例如:
exports.main = async function() {
await ((test) => {
return new Promise((resolve, reject) => {
setTimeout(() => { resolve(test) }, 3000)
});
})('測試')
}
proxy——數據代理
Gracejs支持兩種數據代理場景:
- 單純的數據代理,任意請求到后端接口,然后返回json數據(也包括文件流請求到后端,后端返回json數據);
- 文件代理,請求后端接口,返回一個文件(例如驗證碼圖片);
下面逐一介紹兩種代理模式的使用方法。
1、 數據代理
數據代理可以在控制器中使用 this.proxy 方法:
this.proxy(object|string,[opt])
使用方法
this.proxy 方法返回的是一個Promise,所以這里你可以根據當前Controller的類型使用 async/await 或者 Generator 實現異步并發。例如:
async/await:
exports.demo = async function () {
await this.proxy({ /* ... */ })
}
Generator:
exports.demo = function * () {
yield this.proxy({ /* ... */ })
}
為了使語法更簡便,可以在執行 this.proxy 之后,直接在上下文中的 backData 字段中獲取到數據。例如:
exports.demo = async function () {
await this.proxy({
userInfo:'github:post:user/login/oauth/access_token?client_id=****',
otherInfo:'github:other/info?test=test',
})
console.log(this.backData);
/**
* {
* userInfo : {...},
* otherInfo : {...}
* }
*/
}
Generator 方法亦然。
此外,如果要獲取proxy的請求頭信息,你可以在proxy方法返回的內容中獲取到,例如:
exports.demo = async function (){
let res = await this.proxy({
userInfo:'github:post:user/login/oauth/access_token?client_id=****',
otherInfo:'github:other/info?test=test',
});
console.log(res);
/**
* {
* userInfo : {
* headers: {...} // 頭信息
* body: {...} // 未處理的response body
* ... // ...
* },
* otherInfo : {...}
* }
*/
}
使用場景一:多個數據請求的代理
可以發現,上文的案例就是多個數據同時請求的代理方案,這里也就是 異步并發 獲取數據的實現。使用 this.proxy 方法實現多個數據異步并發請求非常簡單:
exports.demo = async function (){
await this.proxy({
userInfo:'github:post:user/login/oauth/access_token?client_id=****',
otherInfo:'github:other/info?test=test',
});
console.log(this.backData);
/**
* {
* userInfo : {...},
* otherInfo : {...}
* }
*/
}
然后,proxy的結果會默認注入到上下文的 this.backData 對象中。
使用場景二:單個數據請求的代理
如果只是為了實現一個接口請求代理,可以這么寫:
exports.demo = async function (){
await this.proxy('github:post:user/login/oauth/access_token?client_id=****');
}
這樣proxy請求返回的數據體會直接賦值給 this.body ,也就是將這個請求直接返回給客戶端。
說明
github:post:user/login/oauth/access_token?client_id=**** 說明如下:
- github : 為在 config/main.*.js 的 api 對象中進行配置;
- post : 為數據代理請求的請求方法,該參數可以不傳,默認為 get
- path : 后面請求路徑中的query參數會覆蓋當前頁面的請求參數(this.query),將query一同傳到請求的接口
- 你也可以寫完整的路徑: {userInfo:'https://api.github.com/user/login?test=test'}
另外, this.proxy 的形參說明如下:
參數名 | 類型 | 默認 | 說明 |
---|---|---|---|
dest | Object | this.backData | 指定接收數據的對象,默認為 this.backData |
conf | Obejct | {} | this.proxy使用 Request.js 實現,此為傳給request的重置配置(你可以在這里設置接口超時時間: conf: { timeout: 25000 } ) |
form | Object | {} | 指定post方法的post數據,默認為當前頁面的post數據 |
關于this.proxy方法還有很多有趣的細節,推薦有興趣的同學看源碼: https://github.com/xiongwilee/koa-grace/tree/v2.x/middleware/proxy
2、 文件代理
文件代理可以在控制器中使用 this.fetch 方法:
this.fetch(string)
文件請求代理也很簡單,比如如果需要從github代理一個圖片請求返回到瀏覽器中,參考: http://feclub.cn/user/avatar?img=https://avatars.githubusercontent.com/u/1962352?v=3 , 或者要使用導出文件的功能:
exports.avatar = async function (){
await this.fetch(imgUrl);
}
這里需要注意的是: 在this.fetch方法之后會直接結束response, 不會再往其他中間件執行 。
views——視圖層
默認的模板引擎為 swig ,但swig作者已經停止維護;你可以在 config/main.*.js 中配置 template 屬性想要的模板引擎:
// 模板引擎配置
template: 'nunjucks'
你還可以根據不同的模塊配置不同的模板引擎:
template: {
blog:'ejs'
}
目前支持的模板引擎列表在這里: consolidate.js#supported-template-engines
在控制器中調用 this.render 方法渲染模板引擎:
exports.home = await function () {
await this.render('dashboard/site_home',{
breads : ['站點管理','通用'],
userInfo: this.userInfo,
siteInfo: this.siteInfo
})
}
模板文件在模塊路徑的 /views 目錄中。
注意一點:Gracejs渲染模板時,默認會將 main.*.js 中constant配置交給模板數據;這樣,如果你想在頁面中獲取公共配置(比如:CDN的地址)的話就可以在模板數據中的 constant 子中取到。
static——靜態文件服務
靜態文件的使用非常簡單,將 /static/**/ 或者 /*/static/* 的靜態文件請求代理到了模塊路徑下的 /static 目錄:
// 配置靜態文件路由
app.use(Middles.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
注意兩點:
- 靜態文件端口和當前路由的端口一致,所以 /static/**/ 或者 /*/static/* 形式的路由會是無效的;
- 推薦在生產環境中,使用Nginx做靜態文件服務,購買CDN托管靜態文件;
mock——Mock數據
MOCK功能的實現其實非常簡單,在開發環境中你可以很輕易地使用MOCK數據。
以demo模塊為例,首先在 main.development.js 配置文件中添加proxy配置:
// controller中請求各類數據前綴和域名的鍵值對
api: {
// ...
demo: 'http://${ip}:${port}/__MOCK__/demo/'
// ...
}
然后,在demo模塊中添加 mock 文件夾,然后添加 test.json :
文件結構:
.
├── controller
├── mock
| └── test.json
├── static
└── views
文件內容(就是你想要的請求返回內容):
在JSON文件內容中也可以使用注釋:
/*
* 獲取用戶信息接口
*/
{
code:0 // 這是code
}
然后,你可以打開瀏覽器訪問: http://${ip}:${port}/__MOCK__/demo/test 驗證是否已經返回了test.json里的數據。
最后在你的controller業務代碼中就可以通過proxy方法獲取mock數據了:
this.proxy({
test:'demo:test'
})
注意:
- 如果你的mock文件路徑是/mock/test/subtest.json 那么proxy路徑則是:test/subtest;
- 強烈建議將mock文件統一為真正的后端請求路徑,這樣以實現真實路徑的mock;
可以參考這個: koa-grace中的mock功能的示例
secure——安全模塊
考慮到用戶路由完全由Nodejs托管以后,CSRF的問題也得在Nodejs層去防護了。此前寫過一片文章: 前后端分離架構下CSRF防御機制 ,這里就只寫使用方法,不再詳述原理。
在Gracejs中可以配置:
// csrf配置
csrf: {
// 需要進行xsrf防護的模塊名稱
module: []
}
然后,在業務代碼中,獲取名為: grace_token 的cookie,以post或者get參數回傳即可。當然,如果你不想污染ajax中的參數對象,你也可以將這個cookie值存到 x-grace-token 頭信息中。
Gracejs監聽到post請求,如果token驗證失效,則直接返回錯誤。
mongo——簡單的數據庫
請注意:不推薦在生產環境中使用數據庫功能
在Gracejs中使用mongoDB非常簡單,當然沒有做過任何壓測,可能存在性能問題。
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關閉
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、 在控制器中調用數據庫
在控制器中使用非常簡單,主要通過 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 = async function (){
let cates = await 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 = await 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的結果,兩者并發執行
xload——文件上傳下載
請注意:不推薦在生產環境中使用文件上傳下載功能
與數據庫功能一樣,文件上傳下載功能的使用非常簡單,但不推薦在生產環境中使用。因為目前僅支持在單臺服務器上使用數據庫功能,如果多臺機器的服務就有問題了。
如果需要在線上使用上傳下載功能,你可以使用proxy的方式pipe到后端接口,或者通過上傳組件直接將文件上傳到后端的接口。
1、文件上傳
方法:
this.upload([opt])
示例:
exports.aj_upload = async function() {
await this.bindDefault();
let files = await 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;
}
2、文件下載
方法:
this.download(filename, [opt])
示例:
exports.download = async function() {
await this.download(this.query.file);
}
其他
Gracejs中幾個核心的中間件都介紹完畢。此外,還有幾個中間件不做詳細介紹,了解即可:
- gzip實現 :使用gzip壓縮response中的body;
- http body內容解析 :解析request中的body,存到 this.request.body 字段中;
- 簡單的session實現 :通過內存或者redis保存session,不推薦在生產環境中使用;生產環境的session服務由后端自行完成。
最后,關于Gracejs的運維部署在這里不再詳述,推薦使用 pm2 , 不用擔心重啟server期間服務不可用 。
五、前端構建
到這里,整個前后端服務的搭建都介紹完了。
在介紹如何結合Gracejs進行前端構建之前,先提一下:這種“更徹底”的前后端分離方案相比于基于MVVM框架的單頁面應用具體有什么不同呢?
個人認為有以下幾點:
- 運維部署更靈活 基于Nodejs server的服務端構建,服務器的部署可以與后端機器獨立出來。而且后端同學就僅僅需要關注接口的實現。
- 前端技術棧更統一 比如:PHP部署頁面路由,前端通過MVVM框架實現,前端還需要學習PHP語法來實現后端路由。
- 前端架構和選型更便捷 比如你可以很容易通過模板引擎完成BigPipe的架構,你也可以從內網異步并發獲取首屏數據。
當然Gracejs是只是服務端框架,前端架構如何選型,隨你所愿。
Boilerplate
目前已經有基于Vue和requirejs的boilerplate。
-
gulp-requirejs-boilerplate Requirejs supported. (by @xiongwilee )
-
grace-vue-webpack-boilerplate Both Vue@1.x & Vue@2.x supported. (by @thunf )
-
grace-vue2-webpack-boilerplate Vue@2.x supported.(by @haoranw )
這里以基于Vue的構建為例。
目錄結構
一個完整的依賴基于vue+Gracejs的目錄結構推薦使用這種模式:
.
├── app
│ └── demo
│ ├── build
│ ├── controller
│ ├── mock
│ ├── static
│ ├── views
│ └── vues
└── server
├── app
│ └── demo
├── middleware
├── ...
當然,server(即:Gracejs)允許你配置app目錄路徑,你可以放到任意你想要的目錄里。
這里的demo模塊比默認的server下的demo模塊多出來兩個目錄: build 和 vues 。
構建思路
其實,到這里也能猜到如何進行構建了: build 目錄是基于webpack的編譯腳本, vues 目錄是所有的.vue的前端業務文件。
webpack將vues下的vue文件編譯之后產出到 server/app/demo/static 下;其他 controller 等沒有必要編譯的文件,直接使用webpack的復制插件復制到 server/app/demo/ 的對應目錄下即可。
有興趣的同學,推薦看 grace-vue-webpack-boilerplate 下的build實現源碼;當然,需要對webpack和vue有一定的了解。
歡迎同學們貢獻基于 React 、 Angular 的boilerplate,以郵件或者ISSUE的形式通知我們之后,添加到Gracejs的官方文檔中。
結語
自此,洋洋灑灑1w多字,Gracejs終于介紹完畢;有興趣的同學去github賞個star唄: https://github.com/xiongwilee/koa-grace 。
最后,歡迎大家提issue、fork;有任何疑問也可以郵件聯系:xiongwilee[at]foxmail.com。