Vue SSR 学习
一、什么是 SSR,优缺点?
SSR
是 Server-Side Rendering
的缩写,意为服务端渲染,它是指前端页面是由服务端提供并渲染到浏览器;
SSR
的优缺点:
- 优点:
- 更好的
SEO
:服务端渲染会返回完整的 DOM 结构,客户端渲染只有根节点和 js 文件; - 首屏渲染速度快:服务端会把完整的页面返回给前端,无需等 js 下载解析;
- 更好的
- 缺点:
- 加重服务器的负担:需要服务端对页面进行处理返回,增加工作量;
XSS
攻击的风险:服务端拼接字符串返回浏览器会直接执行,容易遭受XSS
攻击;
二、Vue SSR 的实现
Vue SSR 需要服务端的支持,我们可以用 express
简单搭建一个服务器,首先通过 vue-cli 简单创建一个项目 vue-ssr
,然后在项目下新建一个 server
文件夹,server
文件下新建一个 app.js
,app.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 学习相关推荐
- Vue SSR 渲染 Nuxt3 入门学习
Vue SSR 渲染 Nuxt3 入门学习 SPA应用:也就是单页应用,这些多是在客户端的应用,不利于进行SEO优化(搜索引擎优化). SSR应用:在服务端进行渲染,渲染完成后返回给客户端,每个页面有 ...
- 老张 .NetCore与Vue 框架学习
缘起 作为一个.Net攻城狮已经4年有余了,一直不温不火,正好近来项目不是很忙,闲得无聊,搞一搞新技术,一方面是打发无聊的时间,一方面也是督促自己该学习辣!身边的大神都转行的转行,加薪的加薪,本人比较 ...
- 【服务端渲染】之 Vue SSR
前言 笔记来源:拉勾教育 大前端高薪训练营 阅读建议:内容较多,建议通过左侧导航栏进行阅读 Vue SSR 基本介绍 Vue SSR 是什么 官方文档:https://ssr.vuejs.org/ V ...
- 服务器端渲染-Vue SSR搭建
阅读建议:建议通过左侧导航栏进行阅读 文章简介:本文是Vue.js服务器端渲染的另一种解决方案-SSR(Server-Side Rendering)学习笔记 Vue SSR是什么 官方文档解释:Vue ...
- Vue SSR 性能优化实践
齐云雷,微医云服务团队前端工程师,本文是作者在<第二届缤纷前端技术沙龙>分享主题的文字版. 估计大部分读者对标题中的性能优化更感兴趣,可惜我分享的重点其实更多在于实践.实践有深有浅,下面介 ...
- Vue SSR技术方案落地实现—构建同构应用
Vue SSR技术方案落地实现-构建同构应用 一.基本知识扫盲 1.何为服务器端渲染? 1).服务器端渲染:这种技术方案在前端领域处于蛮荒时代就已出现,当时的解决方案主要是后台开发通过模板引擎来设计( ...
- Vue SSR 初探
前段时间弄了一个前后端分离的 vue-koa-demo,最近为这个项目提供了 Vue SSR 的支持.项目比较简单,所以转成 Vue SSR 成本还是不太大的,但是其中也踩了几个坑,在此记录一下. 一 ...
- Vue.js学习系列七——Vue服务器渲染Nuxt学习
我又回来啦~这次我们来学习Vue的服务器渲染SSR. 关于SSR的文章网上很多,一开始看得我云里雾里.然后去Vue.js 服务器渲染指南和nuxt官网看了看,发现文章大多都是搬运官网的内容,真正讲的清 ...
- Vue 新手学习笔记:vue-element-admin 之安装,配置及入门开发
所属专栏: Vue 开发学习进步 说实话都是逼出来的,对于前端没干过ES6都不会的人,vue视频也就看了基础的一些 但没办法,接下来做微服务架构,前端就用 vue,这块你负责....说多了都是泪,脚手 ...
- 了解 Vue SSR 这一篇足以
文章目录 1 - 什么是服务器端渲染? 1.1 新建server文件夹 1.2 生成一个node项目 1.3 安装express 1.4 服务端渲染小案例 1.5 运行查看效果 1.6 打开浏览器 1 ...
最新文章
- 计算机组成原理2套题,计算机组成原理试卷及答案2套.doc
- OpenGL拾取操作流程
- 基于Java Swing的仪表盘实现
- 产品网络推广方案之网站导航栏如何设置会更利于排名提升?
- 【比较面】真核生物和原核生物的异同
- C++中set和map的erase用法
- 两校合并!新部属大学,来了!
- php timesheet,vue版本的timesheet图表
- [luoguP1013] 进制位(搜索)
- java 创建ppt文件怎么打开文件,JAVA读取PPT文件
- 微信封杀下的互联网江湖
- 27.卷1(套接字联网API)---IP选项
- 个人博客存在的三种形式
- 计算机简史:你想不通去脉 是因为你不了解来龙
- iOS 9 Spotlight搜索 OC版
- xsh报告-推荐系统
- 3D游戏编程作业10
- Logger.error不打印错误堆栈信息问题
- 获取手机或电脑GPS位置信息(定位平台)
- python取excel单元格数值_python读取excel文件单元格中的数据返回类型