摘要:為應用增加新的特性和處理新的情況可能都會改變文件的結構。寫一個模板的最佳實踐是,不要在模板中處理數據。在上面這四個文件夾中,主要的測試代碼將是單元測試,這意味著你需要將被測試的代碼與應用分離開來。
前言
Node和Express并不嚴格要求它的應用的文件結構。你可以以任意的結構來組織你的web應用。這對于小應用來說,通常是不錯的,十分易于學習和實驗。
但是,當你的應用在體積和復雜性上都變得越來越高時,情況就變得復雜了。你的代碼可能會變得凌亂。當你的團隊人數增加時,向在同一個代碼庫內寫代碼變得愈發困難,每次合并代碼時都可能會出現各種各樣的沖突。為應用增加新的特性和處理新的情況可能都會改變文件的結構。
一個好的文件結構,應該是每一個不同的文件或文件夾,都分別負責處理不同的任務。這樣,在添加新特性時才會變得不會有沖突。
最佳實踐這里所推薦的結構是基于MVC設計模式的。這個模式在職責分離方面做得非常好,所以讓你的代碼更具有可維護性。在這里我們不會去過多地討論MVC的優點,而是更多地討論如果使用它來建立你的Express應用的文件結構。
例子:
讓我們來看下面這個例子。這是一個用戶可以登錄,注冊,留下評論的應用。以下是他的文件結構。
project/ controllers/ comments.js index.js users.js helpers/ dates.js middlewares/ auth.js users.js models/ comment.js user.js public/ libs/ css/ img/ views/ comments/ comment.jade users/ index.jade tests/ controllers/ models/ comment.js middlewares/ integration/ ui/ .gitignore app.js package.json
這看上去可能有點復雜,但不要擔心。在讀完這篇文章之后,你將會完完全全地理解它。它本質上是十分簡單的。
以下是對這個應用中的根文件(夾)的作用的簡介:
controllers/ – 定義你應用的路由和它們的邏輯
helpers/ – 可以被應用的其他部分所共享的代碼和功能
middlewares/ – 處理請求的Express中間件
models/ – 代表了實現了業務邏輯的數據
public/ – 包含了如圖片,樣式,javascript這樣的靜態文件
views/ – 提供了供路由渲染的頁面模板
tests/ – 用于測試其他文件夾的代碼
app.js – 初始化你的應用,并將所以部分聯接在一起
package.json – 記錄你的應用的依賴庫以及它們的版本
需要提醒的是,除了文件的結構本身,它們所代表的職責也是十分重要的。
Models你應該在modules中處理與數據庫的交互,里面的文件包含了處理數據所有方法。它們不僅提供了對數據的增,刪,改,查方法,還提供了額外的業務邏輯。例如,如果你有一個汽車model,它也應該包含一個mountTyres方法。
對于數據庫中的每一類數據,你都至少應該創建一個對應的文件。對應到我們的例子里,有用戶以及評論,所以我們至少要有一個user model和一個comment model。當一個model變得過于臃腫時,基于它的內部邏輯將它拆分成多個不同的文件通常是一個更好的做法。
你應該保持你的各個model之間保持相對獨立,它們應相互不知道對方的存在,也不應引用對方。它們也不需要知道controllers的存在,也永遠不要接受HTTP請求和響應對象,和返回HTTP錯誤,但是,我們可以返回特定的model錯誤。
這些都會使你的model變得更容易維護。由于它們之間相互沒有依賴,所以也容易進行測試,對其中一個model進行改變也不會影響到其他model。
以下是我們的評論model:
var db = require("../db") // Create new comment in your database and return its id exports.create = function(user, text, cb) { var comment = { user: user, text: text, date: new Date().toString() } db.save(comment, cb) } // Get a particular comment exports.get = function(id, cb) { db.fetch({id:id}, function(err, docs) { if (err) return cb(err) cb(null, docs[0]) }) } // Get all comments exports.all = function(cb) { db.fetch({}, cb) } // Get all comments by a particular user exports.allByUser = function(user, cb) { db.fetch({user: user}, cb) }
它并不引用用戶model。它僅僅需要一個提供用戶信息的user參數,可能包含用戶ID或用戶名。評論model并不關心它到底是什么,它只關心這可以被存儲。
var db = require("../db") , crypto = require("crypto") hash = function(password) { return crypto.createHash("sha1").update(password).digest("base64") } exports.create = function(name, email, password, cb) { var user = { name: name, email: email, password: hash(password), } db.save(user, cb) } exports.get = function(id, cb) { db.fetch({id:id}, function(err, docs) { if (err) return cb(err) cb(null, docs[0]) }) } exports.authenticate = function(email, password) { db.fetch({email:email}, function(err, docs) { if (err) return cb(err) if (docs.length === 0) return cb() user = docs[0] if (user.password === hash(password)) { cb(null, docs[0]) } else { cb() } }) } exports.changePassword = function(id, password, cb) { db.update({id:id}, {password: hash(password)}, function(err, affected) { if (err) return cb(err) cb(null, affected > 0) }) }
除了創建和管理用戶的方法,用戶的model還應該提供身份驗證和密碼管理的方法。再次重申,這些model之間必須相互不知道對方的存在。
Views這個文件夾內包含了所有你的應用需要渲染的模板,通常都是由你團隊內的設計師設計的。
當選擇模板語言時,你可能會有些困難,因為當下可選擇的模板語言太多了。我最喜歡的兩個模板語言是Jade和Mustache。Jade非常擅于生成HTML,它相對于HTML更簡短以及更可讀。它對JavaScript的條件和迭代語法也有強大的支持。Mustache則完全相反,它更專注于模板渲染,而不是很關心邏輯操作。
寫一個模板的最佳實踐是,不要在模板中處理數據。如果你需要在模板中展示處理后的數據,你應該在controller處理它們。同樣地,多余的邏輯操作也應該被移到controller中。
doctype html html head title Your comment web app body h1 Welcome and leave your comment each comment in comments article.Comment .Comment-date= comment.date .Comment-text= comment.textControllers
在這個文件夾里的是所有你定義的路由。它們處理web請求,處理數據,渲染模板,然后將其返回給用戶。它們是你的應用中的其他部分的粘合劑。
通常情況下,你應該至少為你應用的每一個邏輯部分寫一個路由。例如,一個路由來處理評論,另一個路由來處理用戶,等等。同一類的路由最好有相同的前綴,如/comments/all,/comments/new。
有時,什么代碼該寫進controller,什么代碼該寫進model是容易混淆的。最佳的實踐是,永遠不要在controller里直接調用數據庫,應該使用model提供方法來代替之。例如,如果你有一個汽車model,然后想要在某輛車上安上四個輪子,你不應該直接調用db.update(id, {wheels: 4}),而是應該調用類似car.mountWheels(id, 4)這樣的model方法。
以下是關于評論的controller代碼:
var express = require("express") , router = express.Router() , Comment = require("../models/comment") , auth = require("../middlewares/auth") router.post("/", auth, function(req, res) { user = req.user.id text = req.body.text Comment.create(user, text, function (err, comment) { res.redirect("/") }) }) router.get("/:id", function(req, res) { Comment.get(req.params.id, function (err, comment) { res.render("comments/comment", {comment: comment}) }) }) module.exports = router
通常在controller文件夾中,有一個index.js。它用來加載其他的controller,并且定義一些沒有常規前綴的路由,如首頁路由:
var express = require("express") , router = express.Router() , Comment = require("../models/comment") router.use("/comments", require("./comments")) router.use("/users", require("./users")) router.get("/", function(req, res) { Comments.all(function(err, comments) { res.render("index", {comments: comments}) }) }) module.exports = router
這個文件的router加載了你的所有路由。所以你的應用在啟動時,只需要引用它既可。
Middlewares所有的Express中間件都會保存在這個文件夾中。中間件存在的目的,就是提取出一些controller中,處理請求和響應對象的共有的代碼。
和controller一樣,一個middleware也不應該直接調用數據庫方法。而應使用model方法。
以下例子是middlewares/users.js,它用來在請求時加載用戶信息。
User = require("../models/user") module.exports = function(req, res, next) { if (req.session && req.session.user) { User.get(req.session.user, function(err, user) { if (user) { req.user = user } else { delete req.user delete req.session.user } next() }) } else { next() } }
這個middleware使用了用戶model,而不是直接操作數據庫。
下面,是一個身份認證中間件。通過它來阻止未認證的用戶進入某些路由:
module.exports = function(req, res, next) { if (req.user) { next() } else { res.status(401).end() } }
它沒有任何外部依賴。如果你看了上文controller中的例子,你一定已經明白它是如何運作的。
Helpers這個文件夾中包含一些實用的工具方法,被用于model,middleware或controller中。通常對于不同的任務,這些工具方法會保存在不同的文件中。
Public這個文件只用于存放靜態文件。通常是css, 圖片,JS庫(如jQuery)。提供靜態文件的最佳實踐是使用Nginx或Apache作為靜態文件服務器,在這方面,它們通常比Node更出色。
Tests所有的項目都需要有測試,你需要將所有的測試代碼放在一個地方。為了方便管理,你可能需要將這些測試代碼放于幾個不同的子文件夾中。
controllers
helpers
models
middleware
integration
ui
controllers,helpers,models和middlewares都十分清晰。這些文件夾里的文件都應該與源被測試的文件夾中的文件一一對應,且名字相同。這樣更易于維護。
在上面這四個文件夾中,主要的測試代碼將是單元測試,這意味著你需要將被測試的代碼與應用分離開來。但是,integration文件夾內的代碼則主要用來測試你的各部分代碼是否被正確得粘合。例如,是否在正確的地方,調用了正確的中間件。這些代碼通常會比單元測試更慢一些。
ui文件夾內包含了則是UI測試,它與integration文件夾內的測試代碼相似,因為它們的目標都是保證各個部分被正確地粘合。但是,UI測試通常運行在瀏覽器上,并且還需要模擬用戶的操作。所以,通常它比集成測試更慢。
在前四個文件夾的測試代碼,通常都需要盡量多包含各種邊際情況。而集成測試則不必那么細致,你只需保證功能的正確性。UI測試也一樣,它也只需要保證每一個UI組件都正確工作即可。
Other files還剩下app.js和package.json這兩個文件。
app.js是你應用的起始點。它加載其他的一切,然后開始接收用戶的請求。
var express = require("express") , app = express() app.engine("jade", require("jade").__express) app.set("view engine", "jade") app.use(express.static(__dirname + "/public")) app.use(require("./middlewares/users")) app.use(require("./controllers")) app.listen(3000, function() { console.log("Listening on port 3000...") })
package.son文件的主要目的,則是記錄你的應用的各個依賴庫,以及它們的版本號。
{ "name": "Comments App", "version": "1.0.0", "description": "Comments for everyone.", "main": "app.js", "scripts": { "start": "node app.js", "test": "node_modules/.bin/mocha tests/**" }, "keywords": [ "comments", "users", "node", "express", "structure" ], "author": "Terlici Ltd.", "license": "MIT", "dependencies": { "express": "^4.9.5", "jade": "^1.7.0" }, "devDependencies": { "mocha": "^1.21.4", "should": "^4.0.4" } }
你的應用也可以通過正確地配置package.json,然后使用npm start和nam test來啟動和測試你的代碼。詳情參閱:http://browsenpm.org/package.json
最后原文鏈接:https://www.terlici.com/2014/08/25/best-practices-express-structure.html
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86240.html
摘要:前言這將是一個分為兩部分,內容是關于在生產環境下,跑應用的最佳實踐。潛在的攻擊者可以通過它們進行針對性的攻擊。 前言 這將是一個分為兩部分,內容是關于在生產環境下,跑Express應用的最佳實踐。第一部分會關注安全性,第二部分最會關注性能和可靠性。當你讀這篇文章時,假設你已經對Node.js和web開發有所了解,并且對生產環境有了概念。 概覽 生產環境,指的是軟件生命循環中的某個階段。...
摘要:前言這將是一個分為兩部分,內容是關于在生產環境下,跑應用的最佳實踐。第一部分會關注安全性,第二部分則會關注性能和可靠性。關于第一部分,請參閱在生產環境下的最佳實踐安全性。 前言 這將是一個分為兩部分,內容是關于在生產環境下,跑Express應用的最佳實踐。第一部分會關注安全性,第二部分則會關注性能和可靠性。當你讀這篇文章時,會假設你已經對Node.js和web開發有所了解,并且對生產環...
摘要:寫好一個模板的最佳實踐是避免在模板中做任何處理。一個最好的實踐是應該永遠不會直接訪問數據庫。中間件的目的是為了提取常見的代碼,它將會在多個請求中執行,并且通常會修改請求響應對象。它的目的是加載發出請求的用戶。 Models 是你與你的數據庫交互的一些文件。它們包含了你處理你的數據的所有方法和功能。它們不僅僅包含了創建、讀取、更新和刪除的方法,還包含了業務邏輯。例如,如果你有一個 car...
摘要:特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入匯總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應和斧正,會及時更新,平時業務工作時也會不定期更...
閱讀 975·2023-04-26 02:56
閱讀 9502·2021-11-23 09:51
閱讀 1872·2021-09-26 10:14
閱讀 2985·2019-08-29 13:09
閱讀 2158·2019-08-26 13:29
閱讀 574·2019-08-26 12:02
閱讀 3566·2019-08-26 10:42
閱讀 3005·2019-08-23 18:18