成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

[譯]Express應(yīng)用結(jié)構(gòu)的最佳實踐

dreamans / 3356人閱讀

摘要:為應(yīng)用增加新的特性和處理新的情況可能都會改變文件的結(jié)構(gòu)。寫一個模板的最佳實踐是,不要在模板中處理數(shù)據(jù)。在上面這四個文件夾中,主要的測試代碼將是單元測試,這意味著你需要將被測試的代碼與應(yīng)用分離開來。

前言

NodeExpress并不嚴(yán)格要求它的應(yīng)用的文件結(jié)構(gòu)。你可以以任意的結(jié)構(gòu)來組織你的web應(yīng)用。這對于小應(yīng)用來說,通常是不錯的,十分易于學(xué)習(xí)和實驗。

但是,當(dāng)你的應(yīng)用在體積和復(fù)雜性上都變得越來越高時,情況就變得復(fù)雜了。你的代碼可能會變得凌亂。當(dāng)你的團隊人數(shù)增加時,向在同一個代碼庫內(nèi)寫代碼變得愈發(fā)困難,每次合并代碼時都可能會出現(xiàn)各種各樣的沖突。為應(yīng)用增加新的特性和處理新的情況可能都會改變文件的結(jié)構(gòu)。

一個好的文件結(jié)構(gòu),應(yīng)該是每一個不同的文件或文件夾,都分別負(fù)責(zé)處理不同的任務(wù)。這樣,在添加新特性時才會變得不會有沖突。

最佳實踐

這里所推薦的結(jié)構(gòu)是基于MVC設(shè)計模式的。這個模式在職責(zé)分離方面做得非常好,所以讓你的代碼更具有可維護性。在這里我們不會去過多地討論MVC的優(yōu)點,而是更多地討論如果使用它來建立你的Express應(yīng)用的文件結(jié)構(gòu)。

例子:

讓我們來看下面這個例子。這是一個用戶可以登錄,注冊,留下評論的應(yīng)用。以下是他的文件結(jié)構(gòu)。

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

這看上去可能有點復(fù)雜,但不要擔(dān)心。在讀完這篇文章之后,你將會完完全全地理解它。它本質(zhì)上是十分簡單的。

以下是對這個應(yīng)用中的根文件(夾)的作用的簡介:

controllers/ – 定義你應(yīng)用的路由和它們的邏輯

helpers/ – 可以被應(yīng)用的其他部分所共享的代碼和功能

middlewares/ – 處理請求的Express中間件

models/ – 代表了實現(xiàn)了業(yè)務(wù)邏輯的數(shù)據(jù)

public/ – 包含了如圖片,樣式,javascript這樣的靜態(tài)文件

views/ – 提供了供路由渲染的頁面模板

tests/ – 用于測試其他文件夾的代碼

app.js – 初始化你的應(yīng)用,并將所以部分聯(lián)接在一起

package.json – 記錄你的應(yīng)用的依賴庫以及它們的版本

需要提醒的是,除了文件的結(jié)構(gòu)本身,它們所代表的職責(zé)也是十分重要的。

Models

你應(yīng)該在modules中處理與數(shù)據(jù)庫的交互,里面的文件包含了處理數(shù)據(jù)所有方法。它們不僅提供了對數(shù)據(jù)的增,刪,改,查方法,還提供了額外的業(yè)務(wù)邏輯。例如,如果你有一個汽車model,它也應(yīng)該包含一個mountTyres方法。

對于數(shù)據(jù)庫中的每一類數(shù)據(jù),你都至少應(yīng)該創(chuàng)建一個對應(yīng)的文件。對應(yīng)到我們的例子里,有用戶以及評論,所以我們至少要有一個user model和一個comment model。當(dāng)一個model變得過于臃腫時,基于它的內(nèi)部邏輯將它拆分成多個不同的文件通常是一個更好的做法。

你應(yīng)該保持你的各個model之間保持相對獨立,它們應(yīng)相互不知道對方的存在,也不應(yīng)引用對方。它們也不需要知道controllers的存在,也永遠(yuǎn)不要接受HTTP請求和響應(yīng)對象,和返回HTTP錯誤,但是,我們可以返回特定的model錯誤。

這些都會使你的model變得更容易維護。由于它們之間相互沒有依賴,所以也容易進(jìn)行測試,對其中一個model進(jìn)行改變也不會影響到其他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參數(shù),可能包含用戶ID或用戶名。評論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還應(yīng)該提供身份驗證和密碼管理的方法。再次重申,這些model之間必須相互不知道對方的存在。

Views

這個文件夾內(nèi)包含了所有你的應(yīng)用需要渲染的模板,通常都是由你團隊內(nèi)的設(shè)計師設(shè)計的。

當(dāng)選擇模板語言時,你可能會有些困難,因為當(dāng)下可選擇的模板語言太多了。我最喜歡的兩個模板語言是JadeMustacheJade非常擅于生成HTML,它相對于HTML更簡短以及更可讀。它對JavaScript的條件和迭代語法也有強大的支持。Mustache則完全相反,它更專注于模板渲染,而不是很關(guān)心邏輯操作。

寫一個模板的最佳實踐是,不要在模板中處理數(shù)據(jù)。如果你需要在模板中展示處理后的數(shù)據(jù),你應(yīng)該在controller處理它們。同樣地,多余的邏輯操作也應(yīng)該被移到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
Controllers

在這個文件夾里的是所有你定義的路由。它們處理web請求,處理數(shù)據(jù),渲染模板,然后將其返回給用戶。它們是你的應(yīng)用中的其他部分的粘合劑。

通常情況下,你應(yīng)該至少為你應(yīng)用的每一個邏輯部分寫一個路由。例如,一個路由來處理評論,另一個路由來處理用戶,等等。同一類的路由最好有相同的前綴,如/comments/all,/comments/new。

有時,什么代碼該寫進(jìn)controller,什么代碼該寫進(jìn)model是容易混淆的。最佳的實踐是,永遠(yuǎn)不要在controller里直接調(diào)用數(shù)據(jù)庫,應(yīng)該使用model提供方法來代替之。例如,如果你有一個汽車model,然后想要在某輛車上安上四個輪子,你不應(yīng)該直接調(diào)用db.update(id, {wheels: 4}),而是應(yīng)該調(diào)用類似car.mountWheels(id, 4)這樣的model方法。

以下是關(guān)于評論的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,并且定義一些沒有常規(guī)前綴的路由,如首頁路由:

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加載了你的所有路由。所以你的應(yīng)用在啟動時,只需要引用它既可。

Middlewares

所有的Express中間件都會保存在這個文件夾中。中間件存在的目的,就是提取出一些controller中,處理請求和響應(yīng)對象的共有的代碼。

controller一樣,一個middleware也不應(yīng)該直接調(diào)用數(shù)據(jù)庫方法。而應(yīng)使用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,而不是直接操作數(shù)據(jù)庫。

下面,是一個身份認(rèn)證中間件。通過它來阻止未認(rèn)證的用戶進(jìn)入某些路由:

module.exports = function(req, res, next) {
  if (req.user) {
    next()
  } else {
    res.status(401).end()
  }
}

它沒有任何外部依賴。如果你看了上文controller中的例子,你一定已經(jīng)明白它是如何運作的。

Helpers

這個文件夾中包含一些實用的工具方法,被用于model,middlewarecontroller中。通常對于不同的任務(wù),這些工具方法會保存在不同的文件中。

Public

這個文件只用于存放靜態(tài)文件。通常是css, 圖片,JS庫(如jQuery)。提供靜態(tài)文件的最佳實踐是使用NginxApache作為靜態(tài)文件服務(wù)器,在這方面,它們通常比Node更出色。

Tests

所有的項目都需要有測試,你需要將所有的測試代碼放在一個地方。為了方便管理,你可能需要將這些測試代碼放于幾個不同的子文件夾中。

controllers

helpers

models

middleware

integration

ui

controllers,helpers,modelsmiddlewares都十分清晰。這些文件夾里的文件都應(yīng)該與源被測試的文件夾中的文件一一對應(yīng),且名字相同。這樣更易于維護。

在上面這四個文件夾中,主要的測試代碼將是單元測試,這意味著你需要將被測試的代碼與應(yīng)用分離開來。但是,integration文件夾內(nèi)的代碼則主要用來測試你的各部分代碼是否被正確得粘合。例如,是否在正確的地方,調(diào)用了正確的中間件。這些代碼通常會比單元測試更慢一些。

ui文件夾內(nèi)包含了則是UI測試,它與integration文件夾內(nèi)的測試代碼相似,因為它們的目標(biāo)都是保證各個部分被正確地粘合。但是,UI測試通常運行在瀏覽器上,并且還需要模擬用戶的操作。所以,通常它比集成測試更慢。

在前四個文件夾的測試代碼,通常都需要盡量多包含各種邊際情況。而集成測試則不必那么細(xì)致,你只需保證功能的正確性。UI測試也一樣,它也只需要保證每一個UI組件都正確工作即可。

Other files

還剩下app.jspackage.json這兩個文件。

app.js是你應(yīng)用的起始點。它加載其他的一切,然后開始接收用戶的請求。

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文件的主要目的,則是記錄你的應(yīng)用的各個依賴庫,以及它們的版本號。

{
  "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"
  }
}

你的應(yīng)用也可以通過正確地配置package.json,然后使用npm startnam test來啟動和測試你的代碼。詳情參閱:http://browsenpm.org/package.json

最后

原文鏈接:https://www.terlici.com/2014/08/25/best-practices-express-structure.html

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/86240.html

相關(guān)文章

  • []Express在生產(chǎn)環(huán)境下最佳實踐 - 安全性

    摘要:前言這將是一個分為兩部分,內(nèi)容是關(guān)于在生產(chǎn)環(huán)境下,跑應(yīng)用的最佳實踐。潛在的攻擊者可以通過它們進(jìn)行針對性的攻擊。 前言 這將是一個分為兩部分,內(nèi)容是關(guān)于在生產(chǎn)環(huán)境下,跑Express應(yīng)用的最佳實踐。第一部分會關(guān)注安全性,第二部分最會關(guān)注性能和可靠性。當(dāng)你讀這篇文章時,假設(shè)你已經(jīng)對Node.js和web開發(fā)有所了解,并且對生產(chǎn)環(huán)境有了概念。 概覽 生產(chǎn)環(huán)境,指的是軟件生命循環(huán)中的某個階段。...

    Forelax 評論0 收藏0
  • []Express在生產(chǎ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)注性能和可靠性。當(dāng)你讀這篇文章時,會假設(shè)你已經(jīng)對Node.js和web開發(fā)有所了解,并且對生產(chǎn)環(huán)...

    Luosunce 評論0 收藏0
  • Express 項目結(jié)構(gòu)最佳實踐(下)

    摘要:寫好一個模板的最佳實踐是避免在模板中做任何處理。一個最好的實踐是應(yīng)該永遠(yuǎn)不會直接訪問數(shù)據(jù)庫。中間件的目的是為了提取常見的代碼,它將會在多個請求中執(zhí)行,并且通常會修改請求響應(yīng)對象。它的目的是加載發(fā)出請求的用戶。 Models 是你與你的數(shù)據(jù)庫交互的一些文件。它們包含了你處理你的數(shù)據(jù)的所有方法和功能。它們不僅僅包含了創(chuàng)建、讀取、更新和刪除的方法,還包含了業(yè)務(wù)邏輯。例如,如果你有一個 car...

    Pikachu 評論0 收藏0
  • 前端資源系列(4)-前端學(xué)習(xí)資源分享&前端面試資源匯總

    摘要:特意對前端學(xué)習(xí)資源做一個匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 特意對前端學(xué)習(xí)資源做一個匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會及時更新,平時業(yè)務(wù)工作時也會不定期更...

    princekin 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<