前言
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啟動專案
主體設計
- 建立
public
靜態資料夾,views
頁面檢視資料夾。
專案中前端頁面使用了bootstrap
快速搭建,所有需要開放bootstrap樣式檔案和頁面相關的指令碼檔案。在views
資料夾中存放主頁面: index.html
,編輯學生資訊頁面:edit.html
,新增學生頁面:add.html
。
- 建立資料檔案
Db.json
,模擬資料庫存放學生資訊資料
因為我們沒有涉及到資料庫,所以暫時把學生資料存放在Db.json檔案中,在資料檔案中,我們需要一個陣列:students
,用來存放每一個學生的資訊。通常資料檔案都是一個json格式檔案,所以嚴格按照json格式要求來模擬資料。
{
"students": [
{
"id": 1,
"name": "牛魔王",
"age": 18,
"hobbies": "打人"
}
...
]
}
- 建立入口模組: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...')
})
- 抽取路由模組
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
- 封裝資料操作的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
事件模組等。
- 檢視檔案編寫
這裡只需強調一點,在編寫檢視檔案的時候,需要把一些動態的資料用模板語法包裹起來,能夠使得模板引擎正確的渲染模板檔案即可。
- 啟動服務後看結果(略)
後話
需要學習資料和案例原始碼的夥伴可以去GitHub上檢視,如果您對這案例或者學習資料有更好的看法,歡迎issue,或者評論,謝謝。