一、什么是 SSR,优缺点?

SSRServer-Side Rendering 的缩写,意为服务端渲染,它是指前端页面是由服务端提供并渲染到浏览器;

SSR的优缺点:

  • 优点:

    • 更好的 SEO:服务端渲染会返回完整的 DOM 结构,客户端渲染只有根节点和 js 文件;
    • 首屏渲染速度快:服务端会把完整的页面返回给前端,无需等 js 下载解析;
  • 缺点:
    • 加重服务器的负担:需要服务端对页面进行处理返回,增加工作量;
    • XSS攻击的风险:服务端拼接字符串返回浏览器会直接执行,容易遭受 XSS攻击;

二、Vue SSR 的实现

Vue SSR 需要服务端的支持,我们可以用 express简单搭建一个服务器,首先通过 vue-cli 简单创建一个项目 vue-ssr,然后在项目下新建一个 server文件夹,server文件下新建一个 app.jsapp.js的内容如下:

const express = require("express")
const fs = require("fs")
const Vue = require("vue")
// 读取模板
const template = fs.readFileSync("./index.template.html", "utf-8")
// 根据传进去的模板获取 renderer
const renderer = require("vue-server-renderer").createRenderer({ template })const server = express()
server.get("/", async (req, res) => {// 创建一个实例const app = new Vue({data: {title: "Hello SSR!"},template: "<h1 @click='changeTitle'>{{ title }}</h1>",methods: {changeTitle() {this.title = 'after change title'}},})renderer.renderToString(app).then(html => {res.send(html)})
})server.listen(3000, () => {console.log("server is running at 3000")
})

在启动服务器之前,我们需要在终端执行命令 npm i express vue-server-render vue -S安装一下依赖,安装完成之后,在server目录下执行命令 node app.js,服务器启动之后在浏览器输入 localhost:3000就可以看到页面;

index.template.html的代码如下:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>title</title></head><body><!--vue-ssr-outlet--></body>
</html>

实际上,我们在代码里面设置了一个点击事件,但是在页面点击的时候,是没有触发点击事件的,因为 vue-server-renderer没有帮我们做这方面的处理,为了解决这个问题,我们需要客户端再渲染一遍,这一过程可以称为同构

三、服务端结合客户端完成 SSR 渲染

上面讲到我们需要客户端重新渲染一遍页面,因此我们需要 webpack帮我们打包两份 bundle文件,一份用于客户端,一份用于服务端,具体实现接着往下看:

1、安装依赖包

npm install vue-router vue-loader cross-env lodash.merge webpack-node-externals -S

2、在 App.vue 文件中使用路由标签

<template><div id="app"><router-link to="/">首页</router-link><router-link to="/about">关于我</router-link><span @click="handleClick">click me</span><router-view></router-view></div>
</template><script>
export default {name: 'App',methods: {handleClick() {console.log('click me')}}
}
</script><style>
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;
}
</style>

3、新建两个页面 Home.vue 和 About.vue,然后对路由的入口文件进行修改

客户端渲染的路由入口文件,是直接通过一个 new VueRouter生成路由实例,但是在服务端渲染的时候,我们需要把它包装成一个工厂函数,每个用户请求都会生成一个路由实例,因为在服务端渲染,如果没有重新生成实例,而是每个用户共享一个实例的话,会造成很严重的后果,根实例也是一样;

// router/index.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const Home = () => import("../components/Home.vue")
const About = () => import("../components/About.vue")
// 每个用户请求都要创建一个路由实例和根实例
export default function createRouter() {return new Router({// 必须是 history,因为 hash 模式的"#"不会发送给服务器mode: 'history',routes: [{path: '/',component: Home,name: 'home'},{path: '/about',component: About,name: 'about'}]})
}

4、对 main.js 进行修改

import Vue from 'vue'
import App from './App.vue'
import createRouter from './router/index'
Vue.config.productionTip = false
// 每个用户请求都要创建一个路由实例和根实例
export function createApp() {const router = new createRouter()const app = new Vue({router,render: h => h(App)})// router 后面会用到,跟 app 一起返回方便后面使用return { app, router }
}

5、新建 entry-client.js 和 entry-server.js

// entry-client.js 挂载激活 app
import { createApp } from './main.js'
const { app } = createApp()
app.$mount("#app")// entry-server.js
import {createApp} from "./main.js";
export default context => {return new Promise((resolve, reject) => {const { app, router } = createApp();router.push(context.url) ;router.onReady(() => {resolve(app);}, reject);})
}

6、配置 webpack 文件

// vue.config.js
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");// 决定入口是客户端还是服务端,执行 webpack 命令时确认
const TARGET_NODE = process.env.WEBPACK_TARGET === "node";
const target = TARGET_NODE ? "server" : "client";
module.exports = {outputDir: `./dist/${target}`,configureWebpack: () => ({// 将 entry 指向客户端或服务端文件entry: `./src/entry-${target}.js`,devtool: "source-map",// 这允许 webpack 以 Node 使用方式处理动态导入// 在编译 Vue 组件时告知 `vue-loader` 输送面向服务器代码target: TARGET_NODE ? "node" : "web",node: TARGET_NODE ? undefined : false,output: {// 配置服务器端使用 node 的风格构建libraryTarget: TARGET_NODE ? "commonjs2" : 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 }); });}
}

7、修改 package.json

{"scripts": {"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","serve": "cd server && nodemon app.js"}
}

8、对 server / app.js 进行修改

const express = require("express")
const fs = require('fs')
const server = 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 template = fs.readFileSync("./index.template.html", "utf-8");
const renderer = createBundleRenderer(serverBundle, {runInNewContext: false,template,clientManifest,
});// index: false 是为了忽略 dist/client/index.html
server.use(express.static("../dist/client", { index: false }))server.get("*", async (req, res) => {try {// 这里的 context 就是之前 createApp 传入的 contextconst context = {url: req.url,title: 'vue ssr'}// 这里不用 renderToString 是因为防止文件大的情况下出现卡顿现象const stream = renderer.renderToStream(context)let buffer = []stream.on("data", chunk => {buffer.push(chunk)})stream.on("end", () => {res.end(Buffer.concat(buffer))})} catch (err) {res.status(500).send("服务器内部错误")}
})server.listen(3000, () => {console.log("server is running at 3000")
})

9、终端执行命令

npm run build进行打包,生成客户端和服务端相应的 json 文件;

npm run serve启动服务器;

注意:vue-server-renderer 的版本需要跟 vue 的版本一致,否则可能会报错,还有 vue-router 对应版本也不能太高

"vue": "^2.6.14",
"vue-router": "^3.5.3",
"vue-server-renderer": "^2.6.14"

Vue SSR 学习相关推荐

  1. Vue SSR 渲染 Nuxt3 入门学习

    Vue SSR 渲染 Nuxt3 入门学习 SPA应用:也就是单页应用,这些多是在客户端的应用,不利于进行SEO优化(搜索引擎优化). SSR应用:在服务端进行渲染,渲染完成后返回给客户端,每个页面有 ...

  2. 老张 .NetCore与Vue 框架学习

    缘起 作为一个.Net攻城狮已经4年有余了,一直不温不火,正好近来项目不是很忙,闲得无聊,搞一搞新技术,一方面是打发无聊的时间,一方面也是督促自己该学习辣!身边的大神都转行的转行,加薪的加薪,本人比较 ...

  3. 【服务端渲染】之 Vue SSR

    前言 笔记来源:拉勾教育 大前端高薪训练营 阅读建议:内容较多,建议通过左侧导航栏进行阅读 Vue SSR 基本介绍 Vue SSR 是什么 官方文档:https://ssr.vuejs.org/ V ...

  4. 服务器端渲染-Vue SSR搭建

    阅读建议:建议通过左侧导航栏进行阅读 文章简介:本文是Vue.js服务器端渲染的另一种解决方案-SSR(Server-Side Rendering)学习笔记 Vue SSR是什么 官方文档解释:Vue ...

  5. Vue SSR 性能优化实践

    齐云雷,微医云服务团队前端工程师,本文是作者在<第二届缤纷前端技术沙龙>分享主题的文字版. 估计大部分读者对标题中的性能优化更感兴趣,可惜我分享的重点其实更多在于实践.实践有深有浅,下面介 ...

  6. Vue SSR技术方案落地实现—构建同构应用

    Vue SSR技术方案落地实现-构建同构应用 一.基本知识扫盲 1.何为服务器端渲染? 1).服务器端渲染:这种技术方案在前端领域处于蛮荒时代就已出现,当时的解决方案主要是后台开发通过模板引擎来设计( ...

  7. Vue SSR 初探

    前段时间弄了一个前后端分离的 vue-koa-demo,最近为这个项目提供了 Vue SSR 的支持.项目比较简单,所以转成 Vue SSR 成本还是不太大的,但是其中也踩了几个坑,在此记录一下. 一 ...

  8. Vue.js学习系列七——Vue服务器渲染Nuxt学习

    我又回来啦~这次我们来学习Vue的服务器渲染SSR. 关于SSR的文章网上很多,一开始看得我云里雾里.然后去Vue.js 服务器渲染指南和nuxt官网看了看,发现文章大多都是搬运官网的内容,真正讲的清 ...

  9. Vue 新手学习笔记:vue-element-admin 之安装,配置及入门开发

    所属专栏: Vue 开发学习进步 说实话都是逼出来的,对于前端没干过ES6都不会的人,vue视频也就看了基础的一些 但没办法,接下来做微服务架构,前端就用 vue,这块你负责....说多了都是泪,脚手 ...

  10. 了解 Vue SSR 这一篇足以

    文章目录 1 - 什么是服务器端渲染? 1.1 新建server文件夹 1.2 生成一个node项目 1.3 安装express 1.4 服务端渲染小案例 1.5 运行查看效果 1.6 打开浏览器 1 ...

最新文章

  1. 计算机组成原理2套题,计算机组成原理试卷及答案2套.doc
  2. OpenGL拾取操作流程
  3. 基于Java Swing的仪表盘实现
  4. 产品网络推广方案之网站导航栏如何设置会更利于排名提升?
  5. 【比较面】真核生物和原核生物的异同
  6. C++中set和map的erase用法
  7. 两校合并!新部属大学,来了!
  8. php timesheet,vue版本的timesheet图表
  9. [luoguP1013] 进制位(搜索)
  10. java 创建ppt文件怎么打开文件,JAVA读取PPT文件
  11. 微信封杀下的互联网江湖
  12. 27.卷1(套接字联网API)---IP选项
  13. 个人博客存在的三种形式
  14. 计算机简史:你想不通去脉 是因为你不了解来龙
  15. iOS 9 Spotlight搜索 OC版
  16. xsh报告-推荐系统
  17. 3D游戏编程作业10
  18. Logger.error不打印错误堆栈信息问题
  19. 获取手机或电脑GPS位置信息(定位平台)
  20. python取excel单元格数值_python读取excel文件单元格中的数据返回类型

热门文章

  1. 计算机的装机配件,京东买的配件怎么装机自己组装教程
  2. 深圳云计算培训:专科生学习云计算就业前景如何?
  3. iphonex时间显示蓝色_iphonex时间显示蓝色
  4. hybrid 单臂路由
  5. 嵌入式和单片机,两者的区别
  6. WebView 监听 H5 下载
  7. python实现的广域网聊天(无需搭建服务器,基于FICS和socket模块)
  8. 无法ping通别的计算机名,解决主机无法ping通问题
  9. 《第二次曲线创新》李善友教授
  10. 《缠中说禅》炒股经典理论