from Brand Architecture: How To Turn Chaos Into Clarity | by Arek Dvorne
到這個階段,通常我們已經能從頭到尾打造一個全端網路應用程式;然而在開發的過程中,app.js
這支檔案已逐漸成長到有點臃腫而肥大了。
在未來,我們可能會著手越來越大型而複雜的產品及專案,若將所有路由都寫在 app.js
內,將會無限擴充這支程式。不只大幅降地可讀性、也提高了維護及更新的難度。
因此,我們將採用 全端開發者必懂的「產品搭建程序」 提過的——以關注點分離(separation of concerns,SOC)來進行程式碼重構。
簡單來說就是讓程式碼從複雜混亂,走向簡單秩序的過程。有點像天能(TENET)的逆熵(reverse entropy)。
本篇筆記將解決以下問題:
誰適合閱讀:
P.s. 實作案例將以 Todo List 為例。
Restful API from Alpha Camp's material
在 資料操作 CRUD 功能:實作修改、刪除 中曾提過:
由於 HTML form 預設只能使用 GET 及 POST method,然而 Restful API 會採用 PUT 及 DELETE來進行修改及刪除的路由設計,所以在重構的時候會再導入套件來優化。
這邊會快速介紹一個好用的套件—— Method-override。
$ npm install method-override
// 載入 method-override
const methodOverride = require('method-override')
// 設定每一筆請求都會先以 methodOverride 進行前置處理
app.use(methodOverride('_method'))
// 設定在所有路由之前
// app.get ...
以參數的形式帶入路徑中:
<form action="/todos/{{ todo._id }}?_method=PUT" method="POST">
<form action="./todos/{{ this._id }}?_method=DELETE" method="POST" style="display: inline;">
app.put('/todos/:id', (req, res) => {
// ...
})
app.delete('/todos/:id', (req, res) => {
// ...
})
路由器架構圖 from Alpha Camp's material
在路由器的重構方面,我們會將所有路由模組化,並以總路由 routes/index.js
來統一管理。
routes 檔案目錄 from Alpha Camp's material
各資料夾及檔案的命名也是業界實務的慣例:
將以 Express 內建的 express.Router
在 routes/index.js
中進行設定:
// routes/index.js
// 引用 Express 及其路由器
const express = require('express')
const router = express.Router()
// 引用建立好的路由模組
const home = require('./modules/home')
const todos = require('./modules/todos')
// 設定路由
router.use('/', home)
router.use('/todos', todos)
// 匯出路由器
module.exports = router
值得注意的是 router.use('/todos', todos)
這段:在 routes/modules/todos.js
的模組會將重複的 /todos
的路徑,統一移至總路由中設定。
兩個模組的重構方式一樣,這裡首頁路由為例:
// routes/modules/home.js
// 引用 Express 及其路由器
const express = require('express')
const router = express.Router()
// 引用 Todo model
const Todo = require('../../models/todo')
// 定義首頁路由
router.get('/', (req, res) => {
Todo.find()
.lean()
.sort({ _id: 'asc' }) // desc
.then(todos => res.render('index', { todos }))
.catch(error => console.error(error))
})
// 匯出路由模組
module.exports = router
特別注意將路由設定移過來後,原本的 app
都要改成 router
。
所有路由設定都被移轉到路由設定檔後,改用以下路由設定,統一引導至總路由器去安排接收到的請求。
// app.js
// 移除用不到的 const Todo = require('./models/todo')
// 引用路由器
const routes = require('./routes')
// 將 request 導入路由器
app.use(routes)
在專案中,我們不只在主程式 app.js
,也在產生種子資料的 seeder 中設定資料庫連線,這些都是能抽取到專案的環境設定 (configuration)統一管理。
實務慣例上,會放在 config
資料夾中,並新增 mongoose.js
的設定檔,並將所有相關設定直接移入:
// config/mongoose.js
// 引用 mongoose
const mongoose = require('mongoose')
// 連線資料庫
mongoose.connect('mongodb://localhost/todo-list', { useNewUrlParser: true, useUnifiedTopology: true })
// 設定連線狀態
const db = mongoose.connection
db.on('error', () => {
console.log('mongodb error!')
})
db.once('open', () => {
console.log('mongodb connected!')
})
// 匯出連線狀態設定
module.exports = db
在主程式移除所有 mongoose 設定後,僅須將 ./config/mongoose
檔案引用,因為並不會使用到相關的方法,所以不必設定變數:
// app.js
require('./config/mongoose')
而在 seeder 檔案中,僅需保留連線成功狀態設定中的「產生種子資料」的部分:
// models/seeds/todoSeeder.js
// 引用 Todo model
const Todo = require('../todo')
// 連線成功狀態設定
const db = require('../../config/mongoose')
db.once('open', () => {
for (let i = 0; i < 10; i++) {
Todo.create({ name: 'name-' + i })
}
console.log('todoSeeder.js done ^_^')
})
關於本系列更多內容及導讀,請閱讀作者於 Medium 個人專欄 【無限賽局玩家 Infinite Gamer | Publication – 】 上的文章 《用 JavaScript 打造全端產品的入門學習筆記》系列指南。