帶你學Node系列之express-CRUD

pubdreamcc發表於2019-05-24

前言

hello,小夥伴們,我是你們的pubdreamcc,本篇博文出至於我的GitHub倉庫node學習教程資料,歡迎小夥伴們點贊和star,你們的點贊是我持續更新的動力。

GitHub倉庫地址:node學習教程

本篇文章對應的原始碼:Student-Management-System

好了,廢話不多說了,今天繼續我們express的學習~

Student-Management-System

今天我們實現一個案例,用express實現基本的C-R-U-D(增刪改查)學生資訊管理系統。學習這個案例一方面讓我們熟悉業務開發的流程,一方面可以鞏固之前學習的express知識。

檔案說明

  • app.js -- 入口模組

  • router.js -- 路由模組

  • student.js -- 運算元據模組(封裝運算元據的基本API)

  • Db.json -- 模擬資料庫,儲存資料的檔案

  • views資料夾 -- 頁面檢視檔案

  • public資料夾 -- 靜態資原始檔

啟動

  • git clone 克隆專案到本地

  • npm install 進入專案資料夾,下載相關依賴

  • node app.js node啟動專案

主體設計

  1. 建立public靜態資料夾,views頁面檢視資料夾。

專案中前端頁面使用了bootstrap快速搭建,所有需要開放bootstrap樣式檔案和頁面相關的指令碼檔案。在views資料夾中存放主頁面: index.html,編輯學生資訊頁面:edit.html,新增學生頁面:add.html

  1. 建立資料檔案Db.json,模擬資料庫存放學生資訊資料

因為我們沒有涉及到資料庫,所以暫時把學生資料存放在Db.json檔案中,在資料檔案中,我們需要一個陣列:students,用來存放每一個學生的資訊。通常資料檔案都是一個json格式檔案,所以嚴格按照json格式要求來模擬資料。

{
  "students": [
    {
      "id": 1,
      "name": "牛魔王",
      "age": 18,
      "hobbies": "打人"
    }
    ...
  ]
}
  1. 建立入口模組:app.js

在入口模組中啟動伺服器,配置art-template模板引擎,配置express中介軟體body-parser解析post請求提交的資料,最後把路由容器router掛載到app伺服器例項上。因為node中解析程式碼是從上至下,所以路由容器掛載必須在各項配置之後,否則會出錯。

const express = require('express')
const router = require('./router')
const bodyParser = require('body-parser')
const app = express()
// 開發靜態資源
app.use('/public/', express.static('./public'))
app.use('/node_modules/', express.static('./node_modules'))
// 配置模板引擎
app.engine('html', require('express-art-template'))
// 配置body-parser,得到post請求體資料
app.use(bodyParser.urlencoded({ extended: false }))
// 注意:配置模板引擎和body-parser,開放靜態資源必須放在路由容器掛載之前
// 掛載路由容器
app.use(router)
// 繫結埠號,開啟服務
app.listen(3000, () => {
  console.log('running...')
})
  1. 抽取路由模組router.js,專門處理每個路由

因為專案中需要處理多個路由,所以我們分離出一個路由模組,用來處理路由資訊。模組的分離使得node中每個模組各司其職,職能單一,同時也便於後期程式碼維護。

router.js模組中每個路由都需要涉及到運算元據檔案,所以我們在此之外需要封裝一些資料操作的API。封裝非同步 API這裡才是我們學習 Node 的精華部分:奧義之所在。

// 路由模組
const express = require('express')
const Student = require('./student')
// 得到路由容器
const router = express.Router()
/*
  * 下面就是把每條路由都掛載到路由容器中
*/
// 請求'/',顯示全部學生資訊
router.get('/', (req, res) => {
  // 呼叫Student.find()API,獲取所有學生資訊
  。。。
})

// 請求/add',顯示新增學生資訊的介面
router.get('/add', (req, res) => {
  res.render('add.html')
})

// 表單post提交資料到'/add',處理資料後重定向至'/'
router.post('/add', (req, res) => {
  /*
    *獲取表單提交的資料
    *新增到資料檔案中
    *重定向至首頁,顯示新提交的學生資訊
  */
  。。。
})
// 請求'/edit',展示編輯學生資訊介面
router.get('/edit', (req, res) => {
  // 呼叫 Student.findById()API,通過id值獲取學生資訊渲染在頁面上
  。。。
})

// 獲取post請求提交的資料,更新學生資訊,重定向到'/'
router.post('/edit', (req, res) => {
  /**
   * 獲取新修改的post請求提交的學生資訊
   * 處理資料檔案,修改學生資訊
   * 重定向到'/'
   */
})
// 當請求'/delete',根據id值刪除相應的學生資訊,重定向到'/'
router.get('/delete', (req, res) => {
  // 呼叫 Student.deleteById()API,通過id值查詢到對應學生,刪除其資訊
  。。。
})

module.exports = router
  1. 封裝資料操作的API,提取資料操作檔案模組student.js

資料操作的模組,只負責運算元據(增加新資料,修改資料,刪除資料等),不關心業務邏輯。

注意:我們在運算元據檔案的時候,永遠只能是:先讀取出來原有的字串資料,轉換成物件,處理完成之後,再把物件轉換成JSON格式字串,最後再儲存到資料檔案中。檔案中永遠存放的是字串格式的資料

由於是這種操作順序,所以student.js中頻繁使用到node核心模組fs,反覆呼叫fs.readFile()讀取檔案內容,fs.writeFile()修改檔案中的資料。

// 引入fs核心模組
const fs = require('fs')
// 儲存學生資訊
/**
 * 儲存新新增的學生資訊到資料檔案中
 * 引數:
 * 1. 新加的學生資訊物件
 * 2. callback回撥函式,拿到非同步操作的結果
 * callback中的引數:
 * 第一個引數err
 *   成功是null, 失敗是錯誤物件
 *  第二個引數是結果
 *   成功是陣列,失敗是undefined
 */
exports.save = (student, callback) => {
  fs.readFile('./Db.json', 'utf8', (err, data) => {
    if (err) {
      return callback(err)
    }
    // 得到所有學生資訊
    let students = JSON.parse(data).students
    // 為新加的學生新增一個唯一的id屬性(原來最大id屬性值基礎上加1)
    if (students.length === 0) {
      // 如果原始資料檔案中沒有學生資訊
      student.id = 1
    } else {
      student.id = parseInt(students[students.length-1].id) + 1
    }
    students.push(student)
    // 把新增加學生資訊後的物件轉換成字串儲存到資料檔案中
    let dataStr = JSON.stringify({
      students: students
    })
    fs.writeFile('./Db.json', dataStr, err => {
      if (err) {
        // 如果寫入失敗,則把錯誤物件傳遞給它
        return callback(err)
      }
      // 成功就沒錯,所以錯誤物件是 null
      callback(null)
    })
  })
}
。。。

由於篇幅原因,這裡只羅列了新增學生資訊API的實現程式碼。這裡還有一個非常重要的知識點:怎樣獲取到一個函式內部非同步操作的結果?

只能是通過回撥函式。

或許有一些其他的內建模組,包括封裝的API也可以拿到非同步操作的結果,但是其底層都是利用了回撥函式的思想。比如常見的Node內建events事件模組等。

  1. 檢視檔案編寫

這裡只需強調一點,在編寫檢視檔案的時候,需要把一些動態的資料用模板語法包裹起來,能夠使得模板引擎正確的渲染模板檔案即可。

  1. 啟動服務後看結果(略)

後話

需要學習資料和案例原始碼的夥伴可以去GitHub上檢視,如果您對這案例或者學習資料有更好的看法,歡迎issue,或者評論,謝謝。

相關文章