其实这种开发模式,我之前讲 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 并不是万能的~