vue ssr
创建工程 vue cli3
vue create ssr
安装依赖
vue-server-renderer // 渲染器
express // nodejs 服务器
npm i vue-server-renderer express -D
编写服务端脚本
// nodejs 服务器
const express = require('express')
const Vue = require('vue')
// 创建 express 与vue 实例
const app = express()
// 创建渲染实例
const renderer = require('vue-server-renderer').createRenderer()
// 将来将Vue 传给渲染器的实例会得到一个html 内容
const page= new Vue({
data:{title:'服务端渲染'},
template:'<div>{{title}},vue ssr</div>'
})
app.get('/',async(req,res)=>{
try {
const html = await renderer.renderToString(page)
res.send(html)
} catch (error) {
res.status(500).send('服务器内部错误')
}
})
app.listen(3000,()=>{
console.log('渲染服务器启动成功!')
})
采用vue-router
- src下新建一个router文件夹,新建一个index.js
import Vue from 'vue'
import Router from 'vue-router'
import Index from '@/components/Index'
import Detail from '@/components/Detail'
Vue.use(Router)
//将来每次用户请求都需要一个router 实例
export default function createRouter() {
return new Router({
mode: 'history',
routes: [
{ path: '/', component: Index },
{ path: '/detail', component: Detail }
]
})
}
- components 下新建两个文件
Detail.js
<template >
<div>
Detail Page
</div>
</template>
<script>
export default {
}
</script>
<style lang='scss' scoped>
</style>
Index.js
<template >
<div>
index Page
</div>
</template>
<script>
export default {
}
</script>
<style lang='scss' scoped>
</style>
修改App.vue
<template>
<div id="app">
<nav>
<router-link to='/'>首页</router-link>
<router-link to='/detail'>详情页</router-link>
</nav>
<router-view></router-view>
</div>
</template>
入口
// 创建vue 实例
import Vue from 'vue'
import App from './App.vue'
import createRouter from './router'
export default function createApp(){
const router = createRouter()
const app = new Vue({
router,
render:h=>h(App)
})
return {app,router}
}
服务端入口
// 渲染首屏幕
import createApp from './app'
export default context=>{
return Promise((resolve,reject)=>{
const {app,router} = createApp()
// 进入首屏
router.push(context.url)
router.onReady(()=>{
resolve(app)
},reject)
})
}
客户端入口
// 挂载激活app
import createApp from './app'
const {app,router} = createApp()
router.onReady(()=>{
app.$mount('#app')
})
webpack 打包
npm i webpack-node-externals lodash.merge -D
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
const nodeExternals = require("webpack-node-externals")
const merge = require("lodash.merge")
const TARGET_NODE = process.env.WEBPACK_TARGET === 'node'
const target = TARGET_NODE ? "server" :'client'
module.exports = {
css:{
extract: false
},
outputDir:'./dist/'+target,
configureWebpack:()=>({
entry:`./src/entry-${target}.js`,
devtool:'source-map',
target: TARGET_NODE ? 'node' : 'web',
node: TARGET_NODE? undefined:false,
output:{
libraryTarget:TARGET_NODE ? 'commonjs2' :undefined
},
externals:TARGET_NODE ? nodeExternals({
allowlist:[/\.css$/]
}):undefined,
optimization:{
splitChunks:TARGET_NODE ? false :undefined
},
plugins:[TARGET_NODE? new VueSSRServerPlugin() :new VueSSRClientPlugin()]
}),
chainWebpack:config=>{
config.module
.rule("vue")
.use("vue-loader")
.tap(options=>{
merge(options,{
optimizeSSR:false
});
});
}
}
打包脚本
npm i cross-env -D
"scripts": {
"build:client":"vue-cli-service build",
"build:server":"cross-env WEBPACK_TARGET=node vue-cli-service build --node server",
"build":"npm run build:server && npm run build:client"
},
新建一个template 作为服务端渲染的模版 index.temp.html
<!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>
服务端渲染 脚本 更改
// nodejs 服务器
const express = require('express')
const Vue = require('vue')
const fs = require('fs')
// 创建 express 与vue 实例
const app = express()
// 创建渲染实例
const {createBundleRenderer} = require('vue-server-renderer')
// 服务端bundle
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:'ssr test'
}
const html = await renderer.renderToString(context)
res.send(html)
} catch (error) {
res.status(500).send('服务器内部错误')
}
})
app.listen(3000,()=>{
console.log('渲染服务器启动成功!')
})
网友评论