为了完成vue+ssr,本人呕心沥血翻阅很多博主的文档,并在b站看了很多视频之后,经过个人实践,终于整理出一套完整版,无缺失,不报错的改造流程(亲测!!)
从创建=>打包=>上线部署,详细流程。好用的话点个赞吧!!
vue create 项目名称
npm install vue-server-renderer -S
vue-server-renderer和vue必须匹配版本
vue-server-renderer依赖一些Node.js原生提供的api,因此需要配合Node.js使用
import Vue from "vue"
import Router from "vue-router"
import Home from "@/components/Home"
import About from "@/components/About"
Vue.use(Router)
//每次用户请求都需要创建一个新的router实例
//创建createRouter工厂函数
export default function createRouter() {
//创建router实例
return new Router({
mode: "history",
routes: [
{
path: "/",
name: 'home',
component: Home
},
{
path: "/about",
name: 'about',
component: About
}
]
})
}
<template>
<div id="app">
<nav>
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
</nav>
<router-view></router-view>
</div>
</template>
import Vue from "vue"
import App from "./App.vue"
import createRouter from "./router"
//创建createApp工厂函数
export default function createApp() {
const router = createRouter()
//创建vue实例
const app = new Vue({
router,
render: h => h(App),
})
return { app, router }
}
import createApp from "./app"
//context就是地址
export default context => {
return new Promise((resolve, reject) => {
const { app, router } = createApp()
//渲染首屏
router.push(context.url)
router.onReady(() => {
resolve(app)
}, reject)
})
}
import createApp from "./app"
const { app, router } = createApp()
router.onReady(() => {
//挂载激活app
app.$mount("#app")
})
在 public 目录下创建 index.temp.html ,作为渲染 Vue 应用程序时,renderer 生成 HTML 页面包裹容器,来包裹生成的 HTML 标记
vue-ssr-outlet那一坨注释,中间不能有空格!!不然会出问题
通过服务端渲染好的文档节点内容,就是放在那里的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue ssr</title>
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
这里我使用的是express,它是基于原生node的web服务器做了二次的封装,因为原生的node代码比较多,新手可能不太容易理解
使用以下命令安装express
npm install express --save
安装完成后在项目根目录下创建 server.js 文件贴入以下代码,用于搭建web服务器
//nodejs服务器
const express = require("express")
const Vue = require("vue")
const fs = require("fs")
//创建express实例
const app = express()
//创建渲染器
const { createBundleRenderer } = require("vue-server-renderer")
const serverBundle = require("./dist/server/vue-ssr-server-bundle.json")
const clientManifest = require("./dist/client/vue-ssr-client-manifest.json")
const renderer = createBundleRenderer(serverBundle, {
runInNewContext: false,
template: fs.readFileSync("./public/index.temp.html", "utf-8"), //页面模板
clientManifest
})
//中间件处理静态文件请求
app.use(express.static("./dist/client", {index: false}))
//将路由的处理交给vue
app.get("*", async (req, res) => {
try {
const context = {
url: req.url,
title: ""
}
//````````````渲染一个string类型的Vue实例(内容少时)````````````````
// const html = await renderer.renderToString(context)
// res.send(html)
//````````````渲染一个流模式的Vue实例(内容多时)````````````````````
const ssrStream = await renderer.renderToStream(context)
const buffers = []
ssrStream.on("data", (data) => {
buffers.push(data)
})
ssrStream.on("end", () => {
res.end(Buffer.concat(buffers))
})
}catch {
res.status(500).send("服务器内部错误!")
}
})
app.listen(9999, () => {
console.log("服务器渲染成功!")
})
const VueSSRServerPlugin = require("vue-server-renderer/server-plugin")
const VueSSRClientPlugin = require("vue-server-renderer/client-plugin")
//环境变量,决定入口是客户端还是服务端
const TARGRT_NODE = process.env.WEBPACK_TARGET === "node"
const target = TARGRT_NODE ? "server" : "client"
module.exports = {
css: {
extract: false
},
outputDir: "./dist/" + target,
configureWebpack: () => ({
//将 entry 指向应用程序的 server entry 文件
entry: `./src/entry-${target}.js`,
//对 bundle renderer 提供 source map 支持
devtool: "source-map",
//这允许 webpack 以 Node 适用方式(Node-appropriate fashion)处理动态导入(dynamic import)
//并且还会在编译 Vue 组件时,告知 `vue-loader` 输送面向服务器代码(server-oriented code)
target: TARGRT_NODE ? "node" : "web",
node: TARGRT_NODE ? undefined : false,
output: {
//此处告知 server bundle 使用 Node 风格导出模块(Node-style exports)
libraryTarget: TARGRT_NODE ? "commonjs2" : undefined
},
optimization: { splitChunks: TARGRT_NODE ? false : undefined },
//将服务器的整个输出构建为单个 JOSN 文件的插件
//服务端默认文件名为 vue-ssr-server-bundle.json
plugins: [TARGRT_NODE ? new VueSSRServerPlugin() : new VueSSRClientPlugin()]
})
}
安装cross-env 插件:运行跨平台设置和使用环境变量的脚本
npm install cross-env --save
在 package.json 文件中,scripts选项下,添加以下脚本
"scripts": {
"server": "node server",
"build:client": "vue-cli-service build",
"build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build --mode server",
"build": "npm run build:server && npm run build:client"
},
npm run build
npm run server
在浏览器打开http://localhost:9999即可访问,9999为server.js里面我设置的端口号,可以改成自己想设置的端口
新建一个文件夹,将以下内容复制到新文件夹下
package.json文件,打包后的dist文件夹,server.js文件
在服务器安装node,将新文件夹放入相关公司要求的位置,在项目目录执行npm i安装依赖,然后npm run server启动服务即可
注意!项目上线运行时需保持express服务器持续开启