iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 19
0
Modern Web

用 JavaScript 打造全端產品的入門學習筆記系列 第 19

重構程式碼——經典全端實務 IV

Refactor: Turn Chaos Into Clarity

from Brand Architecture: How To Turn Chaos Into Clarity | by Arek Dvorne

為什麼要重構程式碼?

到這個階段,通常我們已經能從頭到尾打造一個全端網路應用程式;然而在開發的過程中,app.js 這支檔案已逐漸成長到有點臃腫而肥大了。

在未來,我們可能會著手越來越大型而複雜的產品及專案,若將所有路由都寫在 app.js 內,將會無限擴充這支程式。不只大幅降地可讀性、也提高了維護及更新的難度。

因此,我們將採用 全端開發者必懂的「產品搭建程序」 提過的——以關注點分離(separation of concerns,SOC)來進行程式碼重構。

簡單來說就是讓程式碼從複雜混亂,走向簡單秩序的過程。有點像天能(TENET)的逆熵(reverse entropy)。

筆記目的

本篇筆記將解決以下問題:

  • 如何語意化路由?
  • 如何重構路由器?
  • 如何重構資料庫連線設定?

誰適合閱讀:

  • 已經能完整打造一個 web app 專案者

P.s. 實作案例將以 Todo List 為例。

 

語意化路由 Restful API

Restful API

Restful API from Alpha Camp's material

資料操作 CRUD 功能:實作修改、刪除 中曾提過:

由於 HTML form 預設只能使用 GET 及 POST method,然而 Restful API 會採用 PUT 及 DELETE來進行修改及刪除的路由設計,所以在重構的時候會再導入套件來優化。

這邊會快速介紹一個好用的套件—— Method-override

引用 Method-override

  1. 以 Terminal 安裝
$ npm install method-override
  1. 設定 Method-override
// 載入 method-override
const methodOverride = require('method-override') 

// 設定每一筆請求都會先以 methodOverride 進行前置處理
app.use(methodOverride('_method'))

// 設定在所有路由之前
// app.get ...

優化 HTTP Method

更新觸發路徑

以參數的形式帶入路徑中:

  • 修改資料
<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) => {
  // ...
})

 

重構路由器 Express.Router

路由器架構圖

路由器架構圖 from Alpha Camp's material

在路由器的重構方面,我們會將所有路由模組化,並以總路由 routes/index.js 來統一管理。

建立路由器架構 routes

routes 檔案目錄

routes 檔案目錄 from Alpha Camp's material

各資料夾及檔案的命名也是業界實務的慣例:

  • 總路由器:routes/index.js
  • home 路由模組: routes/modules/home.js
  • todos 路由模組: routes/modules/todos.js

設定總路由器 express.Router

將以 Express 內建的 express.Routerroutes/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 的路徑,統一移至總路由中設定。

封裝路由模組 router modules

兩個模組的重構方式一樣,這裡首頁路由為例:

// 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)

 

重構資料庫連線設定 Config Mongoose

資料庫連線設定架構圖

在專案中,我們不只在主程式 app.js ,也在產生種子資料的 seeder 中設定資料庫連線,這些都是能抽取到專案的環境設定 (configuration)統一管理。

新增環境設定 config/mongoose.js

config/mongoose.js

實務慣例上,會放在 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

抽取連線設定 app.js & seeder

在主程式移除所有 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 ^_^')
})

 


閱讀更多

Infinite Gamer
關於本系列更多內容及導讀,請閱讀作者於 Medium 個人專欄 【無限賽局玩家 Infinite Gamer | Publication – 】 上的文章 《用 JavaScript 打造全端產品的入門學習筆記》系列指南


上一篇
強化功能:排序 sort、篩選 filter——經典全端實務 V
下一篇
全端開發者需要懂的「Google 搜尋策略」
系列文
用 JavaScript 打造全端產品的入門學習筆記30

尚未有邦友留言

立即登入留言