摘要:寫好一個模板的最佳實踐是避免在模板中做任何處理。一個最好的實踐是應(yīng)該永遠不會直接訪問數(shù)據(jù)庫。中間件的目的是為了提取常見的代碼,它將會在多個請求中執(zhí)行,并且通常會修改請求響應(yīng)對象。它的目的是加載發(fā)出請求的用戶。
Models 是你與你的數(shù)據(jù)庫交互的一些文件。它們包含了你處理你的數(shù)據(jù)的所有方法和功能。它們不僅僅包含了創(chuàng)建、讀取、更新和刪除的方法,還包含了業(yè)務(wù)邏輯。例如,如果你有一個 car model,你可以有一個 mountTyres 方法。
在你的數(shù)據(jù)庫中,針對每種類型的數(shù)據(jù),你應(yīng)該創(chuàng)建至少一個文件。在我們的例子中,我們有 users 和 comments,因此我們有 user model 和 comment model。有時候,當一個 model 文件很大,更好的做法是基于內(nèi)部的邏輯將這個 model 文件分成好幾個文件。
你應(yīng)該讓你的 models 獨立于外部。models 之間不應(yīng)該相互引用。它們不需要知道哪個 controller 調(diào)用它們。它們永遠不要接收 request 或 reponse 對象,它們永遠不要返回 http 的錯誤,但是它們應(yīng)該返回 model 的錯誤。
所有的這些將會使你的 models 更好維護。因為它們是獨立的,所以可以很好地測試它們。Models 可以移動到任何需要用到它的地方。改變一個 model,不會應(yīng)該其他的東西,因為它是獨立的。
基于上面提的的點,讓我們來看看如何實現(xiàn)我們例子中的 model。下面是 comment model。
var db = require("../db") // Create new comment in your database and return its id // 在你的數(shù)據(jù)庫中創(chuàng)建一條新的 comment 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) }
user model 沒有包含進來。comment model 不關(guān)心它是什么,它僅僅關(guān)心它怎么存儲。
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) }) }
除了創(chuàng)建和管理用戶所需要的功能之外,那還有用于用戶身份驗證和密碼管理的方法。再一次的,這個 model 不知道已經(jīng)存在的其他的 model、controller 或者應(yīng)用的其他部分。
Views這個文件夾包含了你應(yīng)用所有需要渲染的模板。通常,團隊中的設(shè)計師會在這里工作。
你想每一個 controllers 所對應(yīng)的模板都有一個子文件夾。這樣的話,你將會為相同的任務(wù)組合模板。
選擇一個模板語言會讓人困惑,因為有很多的選擇。我們最喜歡的模板語言,是 Jade 和 Mustache,我們一直在用。Jade 很適合生成 html 頁面。它使得寫 html 標簽更短和更加可讀。針對于條件和迭代,它也可以使用 JavaScript。Mustache 在另外一方面,專注于渲染各種各樣的模板,它提供了盡可能少的邏輯運算符并且處理數(shù)據(jù)的方法很少。這使得它非常適合編寫非常干凈的模板,這些模板專注于顯示你的數(shù)據(jù)而不是處理數(shù)據(jù)。
寫好一個模板的最佳實踐是避免在模板中做任何處理。如果你的數(shù)據(jù)需要在顯示之前進行處理,在你的 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.text
如你所見,在渲染這個模板時,數(shù)據(jù)預(yù)計已經(jīng)被處理好了。
Controllers這是一個文件夾,你將會定義你應(yīng)用所有的路由在這個文件夾中。你的 controllers 將會處理 web 請求,將模板提供給用戶,并且和你的 models 進行交互,以處理和檢索數(shù)據(jù)。這是膠水,能夠連接和控制你的 web 應(yīng)用。
通常,對于你應(yīng)用中的每一個邏輯部分,你至少會有一個文件。例如,一個文件處理評論,另外一個文件處理關(guān)于用戶的請求等等。來自同一個 controller 的所有路由都有相同的前綴,這是一個好的實踐。例如 /comments/all 和 /comments/new。
有時候很難決定什么應(yīng)該進入 controller,什么應(yīng)該進入 model。一個最好的實踐是應(yīng)該永遠不會直接訪問數(shù)據(jù)庫。它永遠不應(yīng)該調(diào)用 write,update,fetch 這些數(shù)據(jù)庫提供的方法,而應(yīng)該依靠 model 中的方法。例如如果你有一個 car model,你想要把 4 個輪子安裝到這個 car 上,controller 不會調(diào)用 db.update(id, { wheels: 4 }),而是會調(diào)用像 car.mountwheels(id, 4) 這樣的方法。
下面是負責評論的 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 文件夾。它的目的是加載所有其他的 controllers,和可能定義一些沒有相同前綴的路徑,例如 home 頁面路由。
var express = require("express") , router = express.Router() , Comment = require("../models/comment") router.use("/comments", require("./comments")) router.use("/users", require("./users")) // 與 comments 和 users 不同的是,home 頁面不需要前綴(comments 或 users) router.get("/", function(req, res) { Comments.all(function(err, comments) { res.render("index", {comments: comments}) }) }) module.exports = router
這個文件將會處理你所有的路由。你的應(yīng)用在啟動的必須加載的唯一的路由器。
Middlewares在這個文件夾中,你將會存儲所有你 Express 的中間件。中間件的目的是為了提取常見的 controller 代碼,它將會在多個請求中執(zhí)行,并且通常會修改 請求/響應(yīng) 對象。
就像一個 controller,一個中間件永遠不應(yīng)該訪問數(shù)據(jù)庫,相反,對于它要完成的每一項任務(wù),它應(yīng)該使用你的 models。
下面是一個 users 中間件,來自 middlewares/users.js 文件。它的目的是加載發(fā)出請求的用戶。
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() } }
這個中間件使用 user model,并且它沒有直接訪問數(shù)據(jù)庫。
下一步,authorization 中間件,當你想要阻止沒有權(quán)限訪問相同路由的時候,可以用到這個中間件。
module.exports = function(req, res, next) { if (req.user) { next() } else { res.status(401).end() } }
它沒有任何外部的依賴。如果你看看上面的 controllers 文件,你可以看看如何它是如何應(yīng)用的。
Helpers這個文件夾包含實用的代碼,這些代碼被用在多個 models,middlewares 或者 controllers 中,但是 helpers 不屬于 models,middlewares 或 controllers 的范疇。通常,你對不同的常見任務(wù),會有不同的文件。
一個例子就是 helper 文件提供一些方法來管理日期和時間。
Public這個文件件只是提供靜態(tài)文件。通常,它會有子文件夾,像 css,libs,img 用于 css 樣式,圖片和 JavaScript 庫就像 jQuery。這個文件夾能夠提供服務(wù)的最好實踐不是通過你的應(yīng)用,而是通過一個 Nginx 或者 Apache 服務(wù),它們比起 Node 在靜態(tài)文件的服務(wù)更好。
Tests每個項目都需要測試,并且你需要將所有的測試聚集到一起。為了幫助管理它們,你將它們分離在不同的子文件中。
controllers
helpers
models
middlewares
integration
ui
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/95630.html
摘要:為應(yīng)用增加新的特性和處理新的情況可能都會改變文件的結(jié)構(gòu)。寫一個模板的最佳實踐是,不要在模板中處理數(shù)據(jù)。在上面這四個文件夾中,主要的測試代碼將是單元測試,這意味著你需要將被測試的代碼與應(yīng)用分離開來。 前言 Node和Express并不嚴格要求它的應(yīng)用的文件結(jié)構(gòu)。你可以以任意的結(jié)構(gòu)來組織你的web應(yīng)用。這對于小應(yīng)用來說,通常是不錯的,十分易于學(xué)習和實驗。 但是,當你的應(yīng)用在體積和復(fù)雜性上都...
摘要:原文閱讀工程結(jié)構(gòu)最佳實踐組件化按照功能劃分按照組件劃分層次化不要在中寫太多業(yè)務(wù)邏輯,專注層業(yè)務(wù)層要單獨抽出數(shù)據(jù)庫層單獨抽出化把常用組件做成包分離的和配置化環(huán)境感知根據(jù)不同環(huán)境使用不同配置 showImg(https://segmentfault.com/img/bVYQsC?w=2558&h=817); 原文閱讀: nodebestpractices 1 工程結(jié)構(gòu)最佳實踐 1.1 組件...
摘要:前言這將是一個分為兩部分,內(nèi)容是關(guān)于在生產(chǎn)環(huán)境下,跑應(yīng)用的最佳實踐。潛在的攻擊者可以通過它們進行針對性的攻擊。 前言 這將是一個分為兩部分,內(nèi)容是關(guān)于在生產(chǎn)環(huán)境下,跑Express應(yīng)用的最佳實踐。第一部分會關(guān)注安全性,第二部分最會關(guān)注性能和可靠性。當你讀這篇文章時,假設(shè)你已經(jīng)對Node.js和web開發(fā)有所了解,并且對生產(chǎn)環(huán)境有了概念。 概覽 生產(chǎn)環(huán)境,指的是軟件生命循環(huán)中的某個階段。...
摘要:前言這將是一個分為兩部分,內(nèi)容是關(guān)于在生產(chǎn)環(huán)境下,跑應(yīng)用的最佳實踐。第一部分會關(guān)注安全性,第二部分則會關(guān)注性能和可靠性。關(guān)于第一部分,請參閱在生產(chǎn)環(huán)境下的最佳實踐安全性。 前言 這將是一個分為兩部分,內(nèi)容是關(guān)于在生產(chǎn)環(huán)境下,跑Express應(yīng)用的最佳實踐。第一部分會關(guān)注安全性,第二部分則會關(guān)注性能和可靠性。當你讀這篇文章時,會假設(shè)你已經(jīng)對Node.js和web開發(fā)有所了解,并且對生產(chǎn)環(huán)...
摘要:特意對前端學(xué)習資源做一個匯總,方便自己學(xué)習查閱參考,和好友們共同進步。 特意對前端學(xué)習資源做一個匯總,方便自己學(xué)習查閱參考,和好友們共同進步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會及時更新,平時業(yè)務(wù)工作時也會不定期更...
閱讀 821·2021-11-25 09:43
閱讀 1690·2021-09-29 09:42
閱讀 1902·2019-08-30 15:55
閱讀 3423·2019-08-30 15:54
閱讀 2629·2019-08-30 13:20
閱讀 3514·2019-08-29 13:25
閱讀 925·2019-08-28 18:03
閱讀 1787·2019-08-26 13:44