前言
這是該系列文章的第四篇,主要介紹如何使用express+模板引擎做一個完整的案例
模板引擎
模板引擎是爲了使用戶界面與業務數據(內容)分離而產生的,最初源自服務端,後來發展到前端。如果你學過java,應該知道jsp,用過springboot 應該知道thyemleaf,這都屬於模板引擎。當然,這東西現在不是很火了,對前端而言,react,angular,vue的出現,是根本上的變革,react以js爲模板,vue和angular以html爲模板。目前主流的模板引擎有underscore,art-template,jade,ejs… 使用方法大同小異,這裏只介紹art-template
art-template
這裏舉個例子,簡單介紹下art-template在node中的應用
- 項目初始化:新建一個項目,進入後輸入npm init -y
- 安裝: npm i art-template
- 根目錄寫下新建index.html(名稱可自定義),寫入如下內容
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>title</title>
</head>
<body>
<p>姓名->{{ name }}</p>
<p>年齡 ->{{ age }} 歲</p>
<p>涉獵語言->{{each language}} {{ $value }} {{/each}}</p>
</body>
</html>
- 根目錄寫下新建app.js(名稱可自定義),寫入如下內容
var template = require('art-template')//加載模板引擎
var fs = require('fs')//加載fs文件操作模塊
//讀取html文件
fs.readFile('index.html', function (err, data) {//引入模板文件
if (err) {
return console.log('讀取文件失敗');
}
//模板引擎不支持二進制,需要轉成字符串
var result = template.render(data.toString(), {//用render方法填充數據
name: '冷月心',
age: 18,
language: [
'JAVA',
'PHP',
'Node.js'
]
})
console.log(result)
})
- 運行node app.js
- 效果圖
- 可以看到,我們填充的數據都被渲染出來了,這就是模板引擎
綜合案例
下面寫一個綜合小案例,先簡單羅列下涉及的點,做到心中有數
- 工程規範
- 模板引擎和express整合,數據渲染
- 中間件處理post請求
- 404攔截處理
- 忽略favicon.ico請求
- 路由配置
- 靜態資源開放
- 文件上傳
- 文件下載
- 路由重定向
- 其他…
step 1–工程目錄
step2–依賴安裝
npm i express
npm i art-template express-art-template
npm i formidable //處理post請求和文件上傳
step3–整合配置
這裏主要乾了五件事,整合模板引擎,導入並掛載路由,404攔截,忽略favicon.ico請求,開放靜態資源
app.js
//1. 引包
const express = require('express')
//2. 獲取服務器對象
const app = express()
//3. 引入路由
const router = require("./routes")
//4. 整合模板引擎
app.engine('html', require('express-art-template'))
//5. 開放靜態資源
app.use(express.static("public"))
//6. 掛載路由
app.use(router)
//7. 404攔截 寫在最後一個路由後邊
app.use( (req, res)=> {
if(req.url==="/favicon.ico") {
res.send(null)
} else{
res.render("404.html")
}
})
//設置監聽
app.listen(3000, () => {
console.log('run server___')
})
routes/index.js
這裏是路由配置的細節
//引入express formidable
const express = require("express");
const formidable = require('formidable');
//創建路由實例
const router = express.Router()
//路由配置--渲染首頁
router.get('/', (req, res) => {
//注意,這個index.html直接對應views文件夾下的index.html
res.render('index.html', {
title: "hello world"
});
})
//路由配置--渲染form表單
router.get('/form', (req, res) => {
res.render('form.html');
})
//路由配置--渲染form表單
router.get('/backHome', (req, res) => {
//重定向到首頁
res.redirect("/")
})
//路由配置--處理form表單的post請求,並將處理後數據渲染到show.html
router.post('/post', (req, res) => {
const form = new formidable.IncomingForm();
form.parse(req, (err, fields, files) => {
res.render('show.html', {
fields
});
})
})
//導出路由
module.exports = router
此時的項目目錄
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>index</title>
</head>
<body>
<h2>{{title}}</h2>
<!-- 注意,這時候的鏈接是後端路由 -->
<a href="/form">點擊去填寫form表單</a>
</body>
</html>
- 404.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>404</title>
</head>
<body>
404 not found
</body>
</html>
- show.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>show</title>
</head>
<body>
<h2>數據渲染</h2>
<div>
<p>name--{{fields.name}}</p>
<p>age--{{fields.age}}</p>
<!-- 其實可以直接href="/",這裏是爲了演示重定向 -->
<a href="/backHome">返回首頁</a>
</div>
</body>
</html>
- form.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>form</title>
</head>
<body>
<h2>填寫表單</h2>
<!-- 這裏/post也對應後端路由 -->
<form action="/post" method="POST">
<p> <input type="text" name="name" value="冷月心"></p>
<p> <input type="text" name="age" value="18"></p>
<input type="submit" value="提交">
</form>
</body>
</html>
step4–測試邏輯
- 訪問根目錄,跳轉到首頁/
- 點擊鏈接,跳轉到/form
- 點擊提交,表單數據在/show顯示
-
點擊返回首頁,跳轉到首頁/
-
靜態資源開放,訪問/avatar.jpg,顯示圖片
step5–上傳下載
到這裏功能點就實現了一大半,剩下的是上傳和下載,先介紹上傳
- views下新建upload.html,寫入如下內容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>upload</title>
</head>
<body>
<h2>文件上傳</h2>
<form action="/upload" method="POST" enctype="multipart/form-data">
<p> <input type="file" name="pic"></p>
<input type="submit" value="上傳">
</form>
</body>
</html>
- 後臺接口處理
//路由配置--渲染上傳表單
router.get('/upload', (req, res) => {
res.render("upload.html")
})
// 路由配置--處理文件上傳
router.post('/upload', (req, res) => {
const fs = require('fs');
const path = require('path');
const form = new formidable.IncomingForm();
//設置文件上傳存放地址
form.uploadDir = "./public";
//保持拓展名
form.keepExtensions = true;
//執行裏面的回調函數的時候,表單已經全部接收完畢。
form.parse(req, (err, fields, files) => {
//可以在這裏改個名
const oldpath = path.resolve(__dirname, "../public/" + path.basename(files.pic.path));
const newpath = path.resolve(__dirname, "../public/" + files.pic.name)
fs.renameSync(oldpath, newpath)
if (err) {
res.send("上傳失敗", err.message)
return
} else {
res.send("上傳成功")
}
});
})
- 訪問/upload,,選擇一張圖片
- 上傳效果圖
文件下載
到這裏就是最後一個功能點了,下載的邏輯其實很好實現,views下新建download.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>download</title>
</head>
<body>
<a href="/downAvatar">點我下載avatar.jpg</a>
</body>
</html>
- 後臺編寫 downAvatar接口
//路由配置--下載頭像
router.get('/downAvatar', (req, res) => {
const path = require("path")
res.download(path.resolve(__dirname, "../public/avatar.jpg"), (err) => {
if (err) {
res.send("下載失敗")
}
})
})
- 測試,訪問/download
- 點擊
- 源碼獲取 3vbx