亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

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

dreamans / 3425人閱讀

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

前言

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

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

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

最佳實(shí)踐

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

例子:

讓我們來看下面這個(gè)例子。這是一個(gè)用戶可以登錄,注冊(cè),留下評(píng)論的應(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

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

以下是對(duì)這個(gè)應(yīng)用中的根文件(夾)的作用的簡(jiǎn)介:

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

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

middlewares/ – 處理請(qǐng)求的Express中間件

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

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

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

tests/ – 用于測(cè)試其他文件夾的代碼

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

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

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

Models

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

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

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

這些都會(huì)使你的model變得更容易維護(hù)。由于它們之間相互沒有依賴,所以也容易進(jìn)行測(cè)試,對(duì)其中一個(gè)model進(jìn)行改變也不會(huì)影響到其他model。

以下是我們的評(píng)論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。它僅僅需要一個(gè)提供用戶信息的user參數(shù),可能包含用戶ID或用戶名。評(píng)論model并不關(guān)心它到底是什么,它只關(guān)心這可以被存儲(chǔ)。

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)該提供身份驗(yàn)證和密碼管理的方法。再次重申,這些model之間必須相互不知道對(duì)方的存在。

Views

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

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

寫一個(gè)模板的最佳實(shí)踐是,不要在模板中處理數(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

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

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

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

以下是關(guān)于評(píng)論的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文件夾中,有一個(gè)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

這個(gè)文件的router加載了你的所有路由。所以你的應(yīng)用在啟動(dòng)時(shí),只需要引用它既可。

Middlewares

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

controller一樣,一個(gè)middleware也不應(yīng)該直接調(diào)用數(shù)據(jù)庫方法。而應(yīng)使用model方法。

以下例子是middlewares/users.js,它用來在請(qǐng)求時(shí)加載用戶信息。

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()
  }
}

這個(gè)middleware使用了用戶model,而不是直接操作數(shù)據(jù)庫。

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

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

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

Helpers

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

Public

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

Tests

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

controllers

helpers

models

middleware

integration

ui

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

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

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

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

Other files

還剩下app.jspackage.json這兩個(gè)文件。

app.js是你應(yīng)用的起始點(diǎn)。它加載其他的一切,然后開始接收用戶的請(qǐ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)用的各個(gè)依賴庫,以及它們的版本號(hào)。

{
  "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來啟動(dòng)和測(cè)試你的代碼。詳情參閱:http://browsenpm.org/package.json

最后

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/86240.html

相關(guān)文章

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

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

    Forelax 評(píng)論0 收藏0
  • []Express在生產(chǎn)環(huán)境下最佳實(shí)踐 - 性能和可靠性

    摘要:前言這將是一個(gè)分為兩部分,內(nèi)容是關(guān)于在生產(chǎn)環(huán)境下,跑應(yīng)用的最佳實(shí)踐。第一部分會(huì)關(guān)注安全性,第二部分則會(huì)關(guān)注性能和可靠性。關(guān)于第一部分,請(qǐng)參閱在生產(chǎn)環(huán)境下的最佳實(shí)踐安全性。 前言 這將是一個(gè)分為兩部分,內(nèi)容是關(guān)于在生產(chǎn)環(huán)境下,跑Express應(yīng)用的最佳實(shí)踐。第一部分會(huì)關(guān)注安全性,第二部分則會(huì)關(guān)注性能和可靠性。當(dāng)你讀這篇文章時(shí),會(huì)假設(shè)你已經(jīng)對(duì)Node.js和web開發(fā)有所了解,并且對(duì)生產(chǎn)環(huán)...

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

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

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

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

    princekin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<