從0到1構(gòu)建vueSSR項(xiàng)目之node以及vue-cli3的配置
前言
上一次做了路由的相關(guān)配置,原本計(jì)劃今天要做vuex部分,但是想了想,發(fā)現(xiàn)vuex單獨(dú)的客戶端部分穿插解釋起來很麻煩,所以今天改做服務(wù)端部分。
服務(wù)端部分做完,再去做vuex的部分,這樣就會(huì)很清晰。
vue ssr是分兩個(gè)端,一個(gè)是客戶端,一個(gè)是服務(wù)端。
所以要做兩個(gè)cli3的配置。
那么下面就直接開始做吧。
修改package.json的命令
//package.json :client代表客戶端 :server代表服務(wù)端
//使用VUE_NODE來作為運(yùn)行環(huán)境是node的標(biāo)識(shí)
//cli3內(nèi)置 命令 --no-clean 不會(huì)清除dist文件
"scripts": {
"serve:client": " vue-cli-service serve",
"build":"npm run build:server -- --silent && npm run build:client -- --no-clean --silent",
"build:client": "vue-cli-service build",
"build:server": "cross-env VUE_NODE=node vue-cli-service build",
"start:server": "cross-env NODE_ENV=production nodemon nodeScript/index"
}
修改vue.config.js配置
添加完相關(guān)腳本命令之后,我們開始改造cli3配置。
首先要require('vue-server-renderer')
然后再根據(jù)VUE_NODE環(huán)境變量來決定編譯的走向以及生成不同的環(huán)境清單
先做cli3服務(wù)端的入口文件
// src/entry/server.js
import {
createApp
} from '../main.js'
export default context => {
return new Promise((resolve, reject) => {
const {
app,
router
} = createApp(context.data)
//根據(jù)node傳過來的路由 來調(diào)用router路由的指向
router.push(context.url)
router.onReady(() => {
//獲取當(dāng)前路由匹配的組件數(shù)組。
const matchedComponents = router.getMatchedComponents()
//長(zhǎng)度為0就是沒找到該路由所匹配的組件
//可以路由設(shè)置重定向或者傳回node node來操作也可以
if (!matchedComponents.length) {
return reject({
code: 404
})
}
resolve(app)
}, reject)
})
}
這里是cli3的配置
//vue.config.js
const ServerPlugin = require('vue-server-renderer/server-plugin'),//生成服務(wù)端清單
ClientPlugin = require('vue-server-renderer/client-plugin'),//生成客戶端清單
nodeExternals = require('webpack-node-externals'),//忽略node_modules文件夾中的所有模塊
VUE_NODE = process.env.VUE_NODE === 'node',
entry = VUE_NODE ? 'server' : 'client';//根據(jù)環(huán)境變量來指向入口
module.exports = {
css: {
extract: false//關(guān)閉提取css,不關(guān)閉 node渲染會(huì)報(bào)錯(cuò)
},
configureWebpack: () => ({
entry: `./src/entry/${entry}`,
output: {
filename: 'js/[name].js',
chunkFilename: 'js/[name].js',
libraryTarget: VUE_NODE ? 'commonjs2' : undefined
},
target: VUE_NODE ? 'node' : 'web',
externals: VUE_NODE ? nodeExternals({
//設(shè)置白名單
whitelist: /\.css$/
}) : undefined,
plugins: [//根據(jù)環(huán)境來生成不同的清單。
VUE_NODE ? new ServerPlugin() : new ClientPlugin()
]
}),
chainWebpack: config => {
config.resolve
.alias
.set('vue$', 'vue/dist/vue.esm.js')
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
options.optimizeSSR = false;
return options;
});
config.module
.rule('images')
.use('url-loader')
.tap(options => {
options = {
limit: 1024,
fallback:'file-loader?name=img/[path][name].[ext]'
}
return options;
});
}
}
node相關(guān)配置
用于node渲染 必然要攔截get請(qǐng)求的。然后根據(jù)get請(qǐng)求地址來進(jìn)行要渲染的頁面。
大概的方式就是 node攔截所有的get請(qǐng)求,然后將獲取到的路由地址,傳給前臺(tái),然后使用router實(shí)例進(jìn)行push
再往下面看之前 先看一下官方文檔
創(chuàng)建BundleRenderer
createBundleRenderer
將 Vue 實(shí)例渲染為字符串。
renderToString
渲染應(yīng)用程序的模板
template
生成所需要的客戶端或服務(wù)端清單
clientManifest
先創(chuàng)建 服務(wù)端所需要的模板
//public/index.nodeTempalte.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="/favicon.ico" rel="external nofollow" >
<meta charset="utf-8">
<title>vuessr</title>
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
node部分
先創(chuàng)建三個(gè)文件
index.js //入口
proxy.js //代理
server.js //主要配置
//server.js
const fs = require('fs');
const { resolve } = require('path');
const express = require('express');
const app = express();
const proxy = require('./proxy');
const { createBundleRenderer } = require('vue-server-renderer')
//模板地址
const templatePath = resolve(__dirname, '../public/index.nodeTempalte.html')
//客戶端渲染清單
const clientManifest = require('../dist/vue-ssr-client-manifest.json')
//服務(wù)端渲染清單
const bundle = require('../dist/vue-ssr-server-bundle.json')
//讀取模板
const template = fs.readFileSync(templatePath, 'utf-8')
const renderer = createBundleRenderer(bundle,{
template,
clientManifest,
runInNewContext: false
})
//代理相關(guān)
proxy(app);
//請(qǐng)求靜態(tài)資源相關(guān)配置
app.use('/js', express.static(resolve(__dirname, '../dist/js')))
app.use('/css', express.static(resolve(__dirname, '../dist/css')))
app.use('/font', express.static(resolve(__dirname, '../dist/font')))
app.use('/img', express.static(resolve(__dirname, '../dist/img')))
app.use('*.ico', express.static(resolve(__dirname, '../dist')))
//路由請(qǐng)求
app.get('*', (req, res) => {
res.setHeader("Content-Type", "text/html")
//傳入路由 src/entry/server.js會(huì)接收到 使用vueRouter實(shí)例進(jìn)行push
const context = {
url: req.url
}
renderer.renderToString(context, (err, html) => {
if (err) {
if (err.url) {
res.redirect(err.url)
} else {
res.status(500).end('500 | 服務(wù)器錯(cuò)誤');
console.error(`${req.url}: 渲染錯(cuò)誤 `);
console.error(err.stack)
}
}
res.status(context.HTTPStatus || 200)
res.send(html)
})
})
module.exports = app;
//proxy.js
const proxy = require('http-proxy-middleware');
function proxyConfig(obj){
return {
target:'localhost:8081',
changeOrigin:true,
...obj
}
}
module.exports = (app) => {
//代理開發(fā)環(huán)境
if (process.env.NODE_ENV !== 'production') {
app.use('/js/main*', proxy(proxyConfig()));
app.use('/*hot-update*',proxy(proxyConfig()));
app.use('/sockjs-node',proxy(proxyConfig({ws:true})));
}
}
//index.js
const app = require('./server')
app.listen(8080, () => {
console.log('\033[42;37m DONE \033[40;33m localhost:8080 服務(wù)已啟動(dòng)\033[0m')
})
做完這一步之后,就可以預(yù)覽基本的服務(wù)渲染了。
后面就只差開發(fā)環(huán)境的配置,以及到node數(shù)據(jù)的傳遞(vuex)
npm run build npm run start:server 打開localhost:8080 F12 - Network - Doc 就可以看到內(nèi)容

最終目錄結(jié)構(gòu)
|-- vuessr
|-- .gitignore
|-- babel.config.js
|-- package-lock.json
|-- package.json
|-- README.md
|-- vue.config.js
|-- nodeScript //node 渲染配置
| |-- index.js
| |-- proxy.js
| |-- server.js
|-- public//模板文件
| |-- favicon.ico
| |-- index.html
| |-- index.nodeTempalte.html
|-- src
|-- App.vue
|-- main.js
|-- router.config.js//路由集合
|-- store.config.js//vuex 集合
|-- assets//全局靜態(tài)資源源碼
| |-- 備注.txt
| |-- img
| |-- logo.png
|-- components//全局組件
| |-- Head
| |-- index.js
| |-- index.scss
| |-- index.vue
| |-- img
| |-- logo.png
|-- entry//cli3入口
| |-- client.js
| |-- server.js
| |-- 備注.txt
|-- methods//公共方法
| |-- 備注.txt
| |-- mixin
| |-- index.js
|-- pages//源碼目錄
| |-- home
| | |-- index.js
| | |-- index.scss
| | |-- index.vue
| | |-- img
| | | |-- flow.png
| | | |-- head_portrait.jpg
| | | |-- logo.png
| | | |-- vuessr.png
| | |-- vue
| | | |-- index.js
| | | |-- index.scss
| | | |-- index.vue
| | |-- vueCli3
| | | |-- index.js
| | | |-- index.scss
| | | |-- index.vue
| | |-- vueSSR
| | | |-- index.js
| | | |-- index.scss
| | | |-- index.vue
| | |-- vuex
| | |-- index.js
| | |-- index.scss
| | |-- index.vue
| |-- router//路由配置
| | |-- index.js
| |-- store//vuex配置
| |-- all.js
| |-- gather.js
| |-- index.js
|-- static//cdn資源
|-- 備注.txt
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- node.js利用express自動(dòng)搭建項(xiàng)目的全過程
- node koa2 ssr項(xiàng)目搭建的方法步驟
- nodeJs項(xiàng)目在阿里云的簡(jiǎn)單部署
- 云服務(wù)器部署Node.js項(xiàng)目的方法步驟(小白系列)
- node創(chuàng)建Vue項(xiàng)目步驟詳解
- node命令行工具之實(shí)現(xiàn)項(xiàng)目工程自動(dòng)初始化的標(biāo)準(zhǔn)流程
- nodejs對(duì)項(xiàng)目下所有空文件夾創(chuàng)建gitkeep的方法
- PHPStorm中如何對(duì)nodejs項(xiàng)目進(jìn)行單元測(cè)試詳解
- 使用pm2自動(dòng)化部署node項(xiàng)目的方法步驟
- 為什么node.js不適合大型項(xiàng)目
相關(guān)文章
vue elementUI表格控制顯示隱藏對(duì)應(yīng)列的方法
這篇文章主要為大家詳細(xì)介紹了vue elementUI表格控制顯示隱藏對(duì)應(yīng)列的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
axios向后臺(tái)傳遞數(shù)組作為參數(shù)的方法
今天小編就為大家分享一篇axios向后臺(tái)傳遞數(shù)組作為參數(shù)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08
vue實(shí)現(xiàn)一個(gè)簡(jiǎn)單的分頁功能實(shí)例詳解
這篇文章主要介紹了vue實(shí)現(xiàn)一個(gè)簡(jiǎn)單的分頁功能,需要的朋友可以參考下2022-12-12
vue+elementUI的select下拉框回顯為數(shù)字問題
這篇文章主要介紹了vue+elementUI的select下拉框回顯為數(shù)字問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
elementui中樹形表格切換展開不同層級(jí)的示例代碼
這篇文章主要介紹了elementui中樹形表格切換展開不同層級(jí),本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08
vue3簡(jiǎn)單封裝input組件和統(tǒng)一表單數(shù)據(jù)詳解
最近有一個(gè)需求是很多個(gè)表單添加,編輯等操作,會(huì)用到很多input輸入框,所以就想把input進(jìn)行簡(jiǎn)單封裝,這篇文章主要給大家介紹了關(guān)于vue3簡(jiǎn)單封裝input組件和統(tǒng)一表單數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2022-05-05

