什么是ssr
服务端渲染 service render
加载速度,seo
传统vue纯浏览器渲染 浏览器请求,服务器返回一个空的HTML和js给浏览器
这种渲染方式的问题:首页加载时,将所有页面的js都返回了,首屏加载慢。
seo是基于HTML来收录网站,只能爬到一个空的HTML页面
性能问题:先执行js,生成dom,发送请求,请求返回后,浏览器解析数据,操作数据,
重新渲染dom,
优点:能够带来接近原生app的体验,能够前后端分离。
ssr渲染:请求发送到node服务器上
接收到请求后,分析是请求的那个页面,然后node服务端拿到这个页面的组件,将这个页面
设计的js执行完,生成HTML,然后返回给服务器。
ssr需要哪些东西:
将app.js分成两个入口,交给webpack来打包,生成server的bundle在服务端yunx
生成的client的bundle在客户端进行。
ssr的特性
每次访问必须新建一个vue实例,只会触发组件的beforeCreate和created钩子
所以需要客户端的js
核心库
vue和vue-server-render
实现
1.把vue对象变成HTML字符串
test.js
const vue =require('vue');
const app=new vue({
template:'<div>{{num}}</div>',
data:{
num:123
}
const renderer=require('vue-server-render').createRenderer();
renderer.renderToString(app).then(html=>{
console.log(html)
})
})
node test.js
server.js
const vue =require('vue');
const server=require('express')();
const renderer=require('vue-server-render').createRenderer();
const fs=require('fs');
function createApp(url){
//根据URL返回vue实例
if(url=="/"){
url="/index"
}
var json=fs.readFileSync(`json${url}.json`,'utf-8');
var template=fs.readFileSync(`json${url}.html`,'utf-8');
return new vue({
template:template,
data:Json.parse(json).data
})
}
//请求所有地址
server.get('*',function(req,res){
if(req.url!='/favicon.ico'){
const app=createApp(req.url);
renderer.renderToString(app,(err,html)=>{
if(err)
{
res.status(500).end('server error');
return;
}
res.end(html);
})
}
})
server.listen(7070);
node server.js
将vue项目改造成为ssr
1.新建client.js文件和server.js文件
2.新建index.ssr.html文件和index.html文件
在index.ssr.htnl 文件中加注释
<!--vue-ssr-outlet-->
引入js文件
htmlwebpackPlugin.options.file.js
3改造路由
index.js
export function createRouter(){
return new Router({
mode:'history',
routes:[
{
path:'/',
name:'helloword',
component:Helloword
},
{
path:'/test',
name:'test',
component:Test
}
]
})
4.main.js
引入index.js获取到路由
import {createRouter} from ...
var router=createRouter();
改造创建vue根实例方法
export function createApp(){
const app=new Vue({
router,
render:h=>h(App)
})
}
return {app,router}
}
5改造server.js
import{createApp} from ...
//context 是req
export default context=>{
return new Promise((resove,reject)=>{
const {app,router}=createApp();
router.push(context.url);
router.onReady(()=>{
const matchComponets=router.getMatchedComponents();
if(!matchComponets){
return reject({code:404});
}
resolve(app);
},reject)
})
}
改造client.js
import{createApp} from ...
const {app,router}=createApp();
router.onReady(()=>{
//挂载组件
app.$mount("#app");
})
改造webpack打包文件
新建
webpackbuildclicent.js
webpackbuildServer.js
//webpackbuildServer.js
//引入插件
const vuessrserverplugin=require('vue-server-render/server-plugin')
指定入口
entry:{
app:'./src/server.js
},
target:'node',
output:{
libraryTarget:'commonjs2'
}
plugins:[
new VueSSRServerPlugin()
]
//去掉代码分割,复制还有一些配置,太复杂
修改HTMLwebpackplugin的template
为ssr.html
且去掉压缩注释的配置【因为去掉注释,不知道把生成的HTML插入到哪里了】
files:{
js:app.js
}
//webpackbuildclicent.js
//去掉代码分割
//引入插件
const vuessrclientplugin=require('vue-server-render/client-plugin')
plugins:[
new VueSSRClientPlugin()
]
entry:{
app:'./src/client.js
},
删掉出口【因为base.conf.js中有出口,可以合并】
修改package.js
"scripts":{
"build:client":"webpack --config build/webpack.buildclient.js"
"build:server":"webpack --config build/webpack?buildserver.js"
}
打包
npm run build:client
npm run build:server
编写服务 server.js
const express=require('express');
const server=express();
const {createBuldeRenderer}=require('vue-server-renderer');
const path=require('path');
const fs=require('fs');//文件读取模块
//json文件是通知服务器,哪些js是跑在客户端的js,哪些是跑在服务端的js
const serverBundle=require(path.resolve(__dirname,"../dist/vue-ssr-server-bundle.json));
const clientManifest=require(path.resolve(__dirname,"../dist/vue-ssr-client-manifest.json));
const template=fs.readFileSync(path.resolve(__dirname,"../dist/index.ssr.html"),"utf-8");
//生成渲染对象
const renderer=createBuldeRenderer(serverBundle,{
runInNewContest:false,
template:template,
clientManifest:clientManifest//客户端
})
//设置静态目录,服务开启后,这个文件可以被访问
server.use(express.static(path.resolve(__dirname,"./dist")));
//设置访问路由
server.get('*',function(req,res){
if(req.url!='/favicon.ico'){
const context={url:req.url};
const ssrStream=renderer.renderToStream(context);
let buffers=[];
ssrStream.on('error',(err)=>{console.log(err)});
ssrStream.on('error',(data)=>{buffes.push(data)});
ssrStream.on('end',()=>{
res.end(Buffer.concat(buffers));
})
}
})
server.listen(1000)
运行 node server.js
})
nuxt的使用
npx create-nuxt-app
https://segmentfault.com/a/1190000016637877#item-2-2
自己手动搭建一个ssr+vue项目
vue方便的ssr框架nuxt