引言
其實這種開發模式,我之前講 VueSSR 的時候已經提過了。但是,當時只是簡單的提及一個工作目錄 entry-server.js 和 server.js 兩個和服務端相關的文件,用來進行一些後端的查詢之類的。但是如果是簡單的 VueSSR,不能完全適應一些後端的開發,如果需要多寫幾個查詢,項目的耦合性就不高了。現在,我把它補上。
目錄結構
對比我之前將的目錄結構,其實就多了個文件夾。然後,這個文件夾裏面都是和 Express 和 MySQL 相關的文件,例如連接數據庫、路由等等。並且,數據處理部分,我把它簡單地分了兩個部分 Controller、Model(一般我不用哈哈,留着已被不時之需)。所以,一個完整的 Express + VueSSR 的項目結構會是這樣:
build
├── webpack.client.config.js # 用於服務端的打包
├── webpack.server.config.js # 用於客戶端的打包
server # 和服務端相關的代碼,可以理解爲中間層
├── controllers # 進行數據的處理和返回
├── index.js
├── db # 連接數據庫相關
├── index.js
├── routes # 後端路由
├── index.js
├── index.js # 整個項目入口
src
├── store
├── index.js # 不同於傳統的,它是一個工廠函數
├── routes
├── index.js # 不同於傳統的,它是一個工廠函數
├── components # 組件
├── views # 頁面
├── Home
├── index.vue
├── App.vue
├── main.js # 通用 entry(universal entry),不同於傳統的,它是一個工廠函數
├── entry-client.js # 僅運行於客戶端(瀏覽器)
└── entry-server.js # 僅運行於服務器
相比較之前的 VueSSR 其實還有一個小細節,就是我把項目入口文件改成 server 文件夾下的 index.js,其實就是爲了方便閱讀。接下來,來看看 Server 中文件夾具體作用。
作用
1.index.js 項目入口文件
首先,項目的入口遷到 server 文件夾中,也不是平白無故。因爲,要用 express 框架實現中間層的概念,所以相應地也得在項目入口中,實例化 express 並綁定路由,不過需要注意的是原來的 SSR 邏輯保持不變,不過還得設置一些 Content-Type,畢竟我們後面要傳輸 JSON 數據給前端。
// 導入路由文件
const router = require('./routes/index')
// 實例化 express
const app = express()
// 綁定路由
router(app)
...
app.get('*', isProd ? render : (req, res) => {
console.log('請求中')
res.setHeader('Content-Type', 'text/html;charset=utf-8')
readyPromise.then(() => render(req, res))
})
2.routes 路由文件夾
比如說現在有個首頁 home 的 banner 對應的路由(即對應 home.js),它在 express 中路由是這樣的:
const express = require('express')
const route = express.Router()
const home = require('../controllers/home')
route.get('/api/banner', home.getBanner)
module.exports = route
在平常的開發中我們可能存在很多模塊,爲了項目的解耦,我們也需要適當地將不同模塊的路由分開在淡單獨的文件。然後,通過新建一個 index.js 文件來統一導出項目所有路由。
const home = require('./home')
module.exports = function (app) {
app.use(home)
}
3.db 數據庫連接相關文件夾
實現中間層,意味着我們需要查詢數據庫,那就需要連接數據庫。
const mysql = require('mysql')
var pool = mysql.createPool({
host: 'localhost',
port: '3306',
user: 'root',
password: '',
database: 'myblog'
})
const query = function (sql, callback) {
pool.getConnection(function(err, conn) {
if (err) {
callback(err, null, null)
} else {
conn.query(sql, function(qerr, vals, fields) {
// 釋放連接
conn.release()
callback(qerr, vals, fields)
})
}
})
}
// 向外暴露連接數據庫的db對象
module.exports = query
連接的操作很簡單,這裏用了連接池來管理我對數據庫的連接。
4. controllers 文件夾
而 controllers 文件夾中,具體做的就是對查詢的數據進行修飾,返回給前端可以直接使用的數據格式,這裏繼續延續我們前面的 homer 模塊的 benner 路由。
const query = require("../db/index")
function getBanner(req, res, next) {
const sql = "SELECT * FROM banner"
query(sql, (err, result) => {
if (err) {
console.log(`[SELECT ERROR] - `, err.message)
return
}
const data = {
status: 1001,
message: 'success',
data: {
bannerList: result
}
}
res.json(data)
})
}
module.exports = {
getBanner
}
PS:model 文件夾就不講了,沒用過…這裏後端的同學應該更懂
總結
其實,說白了就是將 Express 應用直接嵌入到 VueSSR 項目中。很類似與以前的 MVC,可以這麼說,也可以不那麼說。因爲傳統的 MVC 已經不符合這個時代的需要,並且隨着前端的工程化,Node.js 搭建的服務更適合和 Vue、React 之類的框架搭配使用,它們所用的工具鏈,幾乎都是基於 Node 實現的,所以誇張點就是無縫銜接。當然,話說回來,用這種開發要應場景而進行不同的選擇,Node.js 並不是萬能的~