请先阅读vue-ssr思路篇
1. Vue+koa实现简单服务端渲染
搭建koa服务
yarn add koa koa-router koa-static
const Koa = require('koa');
const Router = require('koa-router');
const Static = require('koa-static');
const app = new Koa();
const router = new Router();
router.get('/',ctx =>{
ctx.body = 'hello world';
})
app.use(router.routes());
app.listen(3000);
后端构建vue并返回
yarn add vue vue-router vuex vue-server-renderer
const Koa = require('koa');
const Router = require('koa-router');
const Static = require('koa-static');
const app = new Koa();
const router = new Router();
const Vue = require('vue');
const VueServerRender = require('vue-server-renderer');
const vm = new Vue({
data(){
return {
msg: 'Hello'
}
},
template: `<div>{{msg}}</div>`
})
let render = VueServerRender.createRenderer();
router.get('/',async ctx =>{
ctx.body = await render.renderToString(vm);
})
app.use(router.routes());
app.listen(3000);
新增模板渲染
- 新建template.html 其中需要挂载点
- 读取模板文件并传给render的配置选项template
template/html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
server.js
const Koa = require('koa');
const Router = require('koa-router');
const Static = require('koa-static');
const app = new Koa();
const router = new Router();
const Vue = require('vue');
const VueServerRender = require('vue-server-renderer');
const fs = require('fs');
const vm = new Vue({
data(){
return {
msg: 'Hello11'
}
},
template: `<div>{{msg}}</div>`
})
const template = fs.readFileSync('./template.html','utf8');
let render = VueServerRender.createRenderer({
template
});
router.get('/',async ctx =>{
ctx.body = await render.renderToString(vm);
})
app.use(router.routes());
app.listen(3000);
2. webpack实现编译Vue项目
配置webpack
loader(module-rules)
- 支持高级语法降级:配置babel-loader(注意使用exclude: /node_modules/排除对第三方进行打包)
- 支持css文件处理:css-loader vue-style-loader
- 支持解析vue组件生成render方法:vue-loader
plugins
- 将定义过的其它规则复制并应用到
.vue
文件里相应语言的块:VueLoaderPlugin - 将打包的js内联进预定义的模板html:HtmlWebpackPlugin
其他
- 预定义文件后缀:resolve-extensions
完整配置
yarn add webpack webpack-cli webpack-dev-server babel-loader @babel/preset-env @babel/core vue-style-loader vue-loader html-webpack-plugin webpack-merge -D
const path = require('path');
// 它的职责是将你定义过的其它规则复制并应用到 .vue 文件里相应语言的块。例如,如果你有一条匹配 /\.js$/ 的规则,那么它会应用到 .vue 文件里的 <script> 块。
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const resolve = (dir)=>{
return path.resolve(__dirname,dir);
}
module.exports = {
entry: resolve('./src/main.js'),
output: {
filename: 'bundle.js',
path: resolve('./dist')
},
resolve: {
extensions:[
'.js',
'.vue'
]
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
},
{
test: /\.vue$/,
use: 'vue-loader'
}
]
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: './plubic/index.html'
})
]
}
构建项目结构
src
│ App.vue
│ list.txt
│ main.js
│
└─components
Bar.vue
Foo.vue
test
npx webpack-dev-server
3. 返回渲染后的html且css、js功能实现(客户端激活)
根据客户端服务端不同需求定义不同的入口及webpack配置文件
编译命令 package.json
"client:dev": "webpack-dev-server --config ./build/webpack.client.js --mode development",
"client:build": "webpack --config ./build/webpack.client.js --mode production",
"server:build": "webpack --config ./build/webpack.server.js --mode production"
配置文件
- webpack.base.js :通用配置
- webpack.client.js :客户端配置
- webpack.server.js : 服务端配置
编译入口
-
main.js :返回app实例
import Vue from 'Vue'; import App from './App'; export default () => { const app = new Vue({ render : h=>h(App) }) return {app}; }
-
server-entry.js :
const WebpackMerge = require('webpack-merge'); const base = require('./webpack.base'); const path = require('path'); const resolve = dir => { return path.resolve(__dirname,dir); } const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = WebpackMerge.merge(base,{ entry: { server: resolve('../src/server-entry.js') }, target: 'node', output: { libraryTarget: 'commonjs2' }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.ssr.html', template: './plubic/index.ssr.html', excludeChunks: ['server'] }) ] })
-
client-entry.js
// 客户端 import createApp from './main'; let {app} = createApp(); app.$mount('#app');
模板html
-
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"></div> </body> </html>
-
index.ssr.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!--vue-ssr-outlet--> </body> </html>
客户端激活
其实很简单,在ssr中,我们需要在入口组件的最外层div上指定id为app,这样编译出来的js才能找到这个挂载点;为什么?假设不设置,我们以index.ssr.html为模板页的时候编译出来的html自然没有id为app的div,这样vue自然挂载不上去