需求 隨著Nodejs的普及,前端開發的開發場景基本可以貫穿界面交互到數據存儲,無縫實現全棧開發。最近在實現一個內部項目管理工具的時候,就嘗試了一把介面和資料庫開發。 什麼是Egg.js Egg.js是阿裡開源的一套Nodejs開發框架。Egg.js官網的介紹是: Egg.js 為企業級框架和應用而 ...
需求
隨著Nodejs的普及,前端開發的開發場景基本可以貫穿界面交互到數據存儲,無縫實現全棧開發。最近在實現一個內部項目管理工具的時候,就嘗試了一把介面和資料庫開發。
什麼是Egg.js
Egg.js是阿裡開源的一套Nodejs開發框架。Egg.js官網的介紹是:
Egg.js 為企業級框架和應用而生,我們希望由 Egg.js 孕育出更多上層框架,幫助開發團隊和開發人員降低開發和維護成本。
為什麼選擇了Egg.js,而不是Koa,Express呢,其實還是為了快速開發,減少搭建項目的時間,Egg.js已經為開發者設計了幾乎最常用的目錄結構,一切傾向於配置化,隱藏一些業務無關的技術細節。開發者可以更加著重考慮業務邏輯,然後在Egg.js和相關插件的支持下,開發功能即可。
Egg.js還提倡『約定優於配置』,這一點我也是很贊同,一致的約定能夠減少不必要的失誤,同時保證了一致的開發體驗,可以方便的維護不同的項目。
初始化項目
Egg.js提供了腳手架快速初始化項目,但是要求npm >=6.1.0,這基本不是問題。
$ mkdir egg-example && cd egg-example $ npm init egg --type=simple $ npm i
然後啟動項目
$ npm run dev $ open http://localhost:7001
目錄設計
因為Egg.js已經做了太多事情,我只需要關註app和config。
├── app | ├── router.js │ ├── controller │ | └── home.js │ ├── service │ | └── user.js │ ├── model │ | └── user.js ├── config | ├── plugin.js | └── config.default.js
app/router.js 用於配置URL路由規則,也就是你訪問的介面地址,對應的是哪個controller的邏輯。
app/controller/** 用於解析用戶的輸入,處理後返回相應的結果,這裡其實可以寫service和model的邏輯,但是按照單一職責的原則,我們會在controller主要放置參數解析和返回值,以及非資料庫操作的邏輯。
app/service/** 用於編寫業務邏輯層,可選,建議使用,你可以理解成對資料庫操作的封裝。
app/model/** 用於定義mongodb的schema,這部分很神奇的是Egg.js已經封裝mongodb鏈接資料庫,並將model綁定到了ctx上,方便調用。
config/config.default.js 用於編寫配置文件,可以配置不同的開發環境,不同的變數,但是因為業務比較單一,內部使用,所以只使用了預設設置。我的項目中配置了跨域、mongoose、csrf,等等。
config.mongoose = { url: "mongodb://127.0.0.1/*****", options: {} }; config.cors = { origin: '*', allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH' } config.security = { csrf: { enable: false, }, };
config/plugin.js 用於配置需要載入的插件,比如egg-mongoose,egg-cors。
module.exports = { mongoose: { enable: true, package: "egg-mongoose" }, cors: { enable: true, package: "egg-cors" } };
這裡需要註意的是,很多博客提供的代碼都是ES6的代碼,在我初始化的模板中是不可行的,如下:
exports.cors = { enable: true, package: 'egg-cors', }
開發項目
基礎搭建好了,開發就變得很簡單了,我遵循的邏輯是:Model-->路由-->Contoller-->Service,先設計資料庫Schema,然後增加新的路由,支持對應的Controller,然後在Service中完成資料庫操作。
router.js
router.get("/api/task", controller.task.index); router.post("/api/task", controller.task.create); router.put("/api/task/:id", controller.task.update); router.delete("/api/task/:id", controller.task.destroy ); // 也可以簡寫為 router.resources('topics', '/api/task', controller.task);
controller中實現的方法具體可以參考下麵的對應關係
Method | Path | Route Name | Controller.Action |
---|---|---|---|
GET | /posts | posts | app.controllers.posts.index |
GET | /posts/new | new_post | app.controllers.posts.new |
GET | /posts/:id | post | app.controllers.posts.show |
GET | /posts/:id/edit | edit_post | app.controllers.posts.edit |
POST | /posts | posts | app.controllers.posts.create |
PUT | /posts/:id | post | app.controllers.posts.update |
DELETE | /posts/:id | post | app.controllers.posts.destroy |
controller/task.js
exports.index = function*() { // ... const result = yield this.service.task.index(this.params); this.body = result; }; exports.create = function*() { // ... const result = yield this.service.task.create(this.request.body); this.body = result; }; exports.update = function*() { // ... const result = yield this.service.task.update(this.params.id, this.request.body); this.body = result; }; exports.destroy = function*() { // ... const result = yield this.service.task.destroy(this.params); this.body = result; };
service/task.js
module.exports = app => { class TaskService extends app.Service { *index(params) { let tasks = yield this.ctx.model.Task.find(params); let result = {}; result.data = tasks; return result; } *create(request) { } *update(id, request) { } *destroy(params) { } } return TaskService; };
model/task.js
module.exports = app => { const mongoose = app.mongoose; const Schema = mongoose.Schema; const TaskSchema = new Schema({ id: {type: Number}, text: {type: String}, type: {type: String}, progress: {type: Number}, open: {type: Boolean}, start_date: {type: String}, owner_id: [{type: String}], duration: {type: Number}, parent: {type: Number} }); return mongoose.model("Task", TaskSchema); };
部署
Egg.js 框架內置了 egg-cluster 來啟動 Master 進程,Master 有足夠的穩定性,不再需要使用 pm2 等進程守護模塊。只需要兩個命令即可:
# 啟動服務 npm start # 關閉服務 npm run stop
結語
站在巨人的肩膀上,讓我們的開發效率倍增,但是還是建議大家先從Koa2學起,對然後對比Egg.js,你就會瞭解它到底封裝了哪些東西,為我們節省了多少工作量,後面還要繼續對Egg.js的插件開發進行瞭解。