Vue2服务端渲染
Vue2
服务端渲染
花费了一个月时间,终于在新养车之家项目中成功部署了vue2服务端渲染(SSR),并且使用上了Vuex 负责状态管理,首屏加载时间从之前4G网络下的1000ms,提升到了现在500-700ms之间,SSR的优势有很多,现在让我来跟你细细道来。
技术栈
服务端:Nodejs(v6.3)
前端框架 Vue2.1.10
前端构建工具:webpack2.2 && gulp
代码检查:eslint
源码:es6
前端路由:vue-router2.1.0
状态管理:vuex2.1.0
服务端通信:axios
日志管理:log4js
项目自动化部署工具:jenkins
Vue2与服务端渲染(SSR)
Vue2.0在服务端创建了虚拟DOM,因此可以在服务端可以提前渲染出来,解决了单页面一直存在的问题:SEO和初次加载耗时较多的问题。同时在真正意义上做到了前后端共用一套代码。
SSR的实现原理
客户端请求服务器,服务器根据请求地址获得匹配的组件,在调用匹配到的组件返回 Promise (官方是preFetch方法)来将需要的数据拿到。最后再通过
- <script>window.__initial_state=data</script>
将其写入网页,最后将服务端渲染好的网页返回回去。
接下来客户端会将vuex将写入的 __initial_state__ 替换为当前的全局状态树,再用这个状态树去检查服务端渲染好的数据有没有问题。遇到没被服务端渲染的组件,再去发异步请求拿数据。说白了就是一个类似React的 shouldComponentUpdate 的Diff操作。
Vue2使用的是单向数据流,用了它,就可以通过 SSR 返回唯一一个全局状态, 并确认某个组件是否已经SSR过了。
开启服务端渲染(SSR)
Web框架目前我们使用的是express,之前使用过一次时间的koa来做SSR,结果发现坑很多,相关的案例太少,有些坑不太好解决,所以为了线上项目的稳定,从而选择了express。
SSR流程图
安装SSR相关
- npm install --save express vue-server-renderer lru-cache es6-promise serialize-javascript vue vue-router axios
vue更新到2.0之后,作者就宣告不再对vue-resource更新,并且vue-resource不支持SSR,所以我推荐使用axios, 在服务端和客户端可以同时使用。
vue2使用了虚拟DOM, 因此对浏览器环境和服务端环境要分开渲染, 要创建两个对应的入口文件。
浏览器入口文件 client-entry.js
使用 $mount 直接挂载
服务端入口文件 server-entry
使用vue的SSR功能直接将虚拟DOM渲染成网页
client-entry.js 文件

- import 'es6-promise/auto';
- import { app, store } from './app';
- store.replaceState(window.__INITIAL_STATE__);
- app.$mount('#app');

在 client-entry.js 文件中引入了app.js, 判断如果在服务端渲染时已经写入状态,则将vuex的状态进行替换,使得服务端渲染的html和vuex管理的数据是同步的。然后将vue实例挂载到html指定的节点中。
server-entry 文件

- import { app, router, store } from './app';
- const isDev = process.env.NODE_ENV !== 'production';
- export default context => {
- const s = isDev && Date.now();
- router.push(context.url);
- const matchedComponents = router.getMatchedComponents();
- if (!matchedComponents.length) {
- return Promise.reject({ code: '404' });
- }
- return Promise.all(matchedComponents.map(component => {
- if (component.preFetch) {
- return component.preFetch(store);
- }
- })).then(() => {
- return app;
- });
- };

在 server-entry 文件中服务端会传递一个context对象,里面包含当前用户请求的url,vue-router 会跳转到当前请求的url中,通过 router.getMatchedComponents( ) 来获得当前匹配组件,则去调用当前匹配到的组件里的 preFetch 钩子,并传递store(Vuex下的状态),会返回一个 Promise 对象,并在then方法中将现有的vuex state 赋值给context,给服务端渲染使用,最后返回vue实例,将虚拟DOM渲染成网页。服务端会将vuex初始状态也生成到页面中。 如果 vue-router 没有匹配到请求的url,直接返回 Promise中的reject方法,传入404,这时候会走到下方renderStream的error事件,让页面显示错误信息。

- // 处理所有的get请求
- app.get('*', (req, res) => {
- // 等待编译
- if (!renderer) {
- return res.end('waiting for compilation... refresh in a moment.');
- }
- var s = Date.now();
- const context = { url: req.url };
- // 渲染我们的Vue实例作为流
- const renderStream = renderer.renderToStream(context);
- // 当块第一次被渲染时
- renderStream.once('data', () => {
- // 将预先的HTML写入响应
- res.write(indexHTML.head);
- });
- // 每当新的块被渲染
- renderStream.on('data', chunk => {
- // 将块写入响应
- res.write(chunk);
- });
- // 当所有的块被渲染完成
- renderStream.on('end', () => {
- // 当vuex初始状态存在
- if (context.initialState) {
- // 将vuex初始状态以script的方式写入到页面中
- res.write(
- `<script>window.__INITIAL_STATE__=${
- serialize(context.initialState, { isJSON: true })
- }</script>`
- );
- }
- // 将结尾的HTML写入响应
- res.end(indexHTML.tail);
- });
- // 当渲染时发生错误
- renderStream.on('error', err => {
- if (err && err.code === '404') {
- res.status(404).end('404 | Page Not Found');
- return;
- }
- res.status(500).end('Internal Error 500');
- });
- })

上面是vue2.0的服务端渲染方式,用流式渲染的方式,将HTML一边生成一边写入相应流,而不是在最后一次全部写入。这样的效果就是页面渲染速度将会很快。还可以引入 lru-cache 这个模块对数据进行缓存,并设置缓存时间,我一般设置15分钟的缓存时间。
可以参考vue ssr 官方演示项目的服务端实现 https://github.com/vuejs/vue-hackernews-2.0/blob/master/server.js>
axios在客户端和服务端的使用
创建2个文件用于客户端和服务端的的通信
create-api-client.js 文件(用于客户端)

- const axios = require('axios');
- let api;
- axios.defaults.timeout = 10000;
- axios.interceptors.response.use((res) => {
- if (res.status >= 200 && res.status < 300) {
- return res;
- }
- return Promise.reject(res);
- }, (error) => {
- // 网络异常
- return Promise.reject({message: '网络异常,请刷新重试', err: error});
- });
- if (process.__API__) {
- api = process.__API__;
- } else {
- api = {
- get: function(target, params = {}) {
- const suffix = Object.keys(params).map(name => {
- return `${name}=${JSON.stringify(params[name])}`;
- }).join('&');
- const urls = `${target}?${suffix}`;
- return new Promise((resolve, reject) => {
- axios.get(urls, params).then(res => {
- resolve(res.data);
- }).catch((error) => {
- reject(error);
- });
- });
- },
- post: function(target, options = {}) {
- return new Promise((resolve, reject) => {
- axios.post(target, options).then(res => {
- resolve(res.data);
- }).catch((error) => {
- reject(error);
- });
- });
- }
- };
- }
- module.exports = api;

create-api-server.js 文件(用于服务端)

- const isProd = process.env.NODE_ENV === 'production';
- const axios = require('axios');
- let host = isProd ? 'http://yczj.api.autohome.com.cn' : 'http://t.yczj.api.autohome.com.cn';
- let cook = process.__COOKIE__ || '';
- let api;
- axios.defaults.baseURL = host;
- axios.defaults.timeout = 10000;
- axios.interceptors.response.use((res) => {
- if (res.status >= 200 && res.status < 300) {
- return res;
- }
- return Promise.reject(res);
- }, (error) => {
- // 网络异常
- return Promise.reject({message: '网络异常,请刷新重试', err: error, type: 1});
- });
- if (process.__API__) {
- api = process.__API__;
- } else {
- api = {
- get: function(target, options = {}) {
- return new Promise((resolve, reject) => {
- axios.request({
- url: target,
- method: 'get',
- headers: {
- 'Cookie': cook
- },
- params: options
- }).then(res => {
- resolve(res.data);
- }).catch((error) => {
- reject(error);
- });
- });
- },
- post: function(target, options = {}) {
- return new Promise((resolve, reject) => {
- axios.request({
- url: target,
- method: 'post',
- headers: {
- 'Cookie': cook
- },
- params: options
- }).then(res => {
- resolve(res.data);
- }).catch((error) => {
- reject(error);
- });
- });
- }
- };
- }
- module.exports = api;

由于在服务端,接口不会主动携带 cookie,所以需要在headers里写入cookie。由于接口数据经常发生变化,所以没有做缓存。
Vue2服务端渲染的更多相关文章
- 如何使用Vue2做服务端渲染
花费了一个月时间,终于在新养车之家项目中成功部署了vue2服务端渲染(SSR),并且使用上了Vuex 负责状态管理,首屏加载时间从之前4G网络下的1000ms,提升到了现在500-700ms之间,SS ...
- 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十五║初探SSR服务端渲染(个人博客二)
缘起 时间真快,现在已经是这个系列教程的下半部 Vue 第 12 篇了,昨天我也简单思考了下,可能明天再来一篇,Vue 就基本告一段落了,因为什么呢,这里给大家说个题外话,当时写博文的时候,只是想给大 ...
- Vue SSR 配合Java的Javascript引擎j2v8实现服务端渲染2创建Vue2+webpack4项目
前提 安装好nodejs并配置好环境变量,最好是 node10,https://nodejs.org/en/download/ 参考我之前的文章 debian安装nodejs Yarn &&a ...
- Vue.js 服务端渲染业务入门实践
作者:威威(沪江前端开发工程师) 本文原创,转载请注明作者及出处. 背景 最近, 产品同学一如往常笑嘻嘻的递来需求文档, 纵使内心万般拒绝, 身体倒是很诚实. 接过需求,好在需求不复杂, 简单构思 后 ...
- 前端性能优化成神之路--SSR(服务端渲染)
Nuxt.js的介绍 Nuxt.js概述 nuxt.js简单的说是Vue.js的通用框架,最常用的就是用来作SSR(服务器端渲染).Vue.js是开发SPA(单页应用)的,Nuxt.js这个框架,用V ...
- 七、SSR(服务端渲染)
使用框架的问题 下载Vue.js 执行Vue.js 生成HTML页面(首屏显示,依赖于vue.js的加载) 以前没有前端框架时,用jsp/php在服务器端进行数据的填充,发送给客户端就是已经填充好的数 ...
- react+redux教程(六)redux服务端渲染流程
今天,我们要讲解的是react+redux服务端渲染.个人认为,react击败angular的真正“杀手锏”就是服务端渲染.我们为什么要实现服务端渲染,主要是为了SEO. 例子 例子仍然是官方的计数器 ...
- 使用 Vue 2.0 实现服务端渲染的 HackerNews
Vue 2.0 支持服务端渲染 (SSR),并且是流式的,可以做组件级的缓存,这使得极速渲染成为可能.同时, 和 2.0 也都能够配合 SSR 提供同构路由和客户端 state hydration.v ...
- React服务端渲染总结
欢迎吐槽 : ) 本demo地址( 前端库React+mobx+ReactRouter ):https://github.com/Penggggg/react-ssr.本文为笔者自学总结,有错误的地方 ...
随机推荐
- GNU Emacs命令速查表
GNU Emacs命令速查表 第一章 Emacs的基本概念 表1-1:Emacs编辑器的主模式 模式 功能 基本模式(fundamental mode) 默认模式,无特殊行为 文本模式(text m ...
- Lisp之根源 --- 保罗格雷厄姆
Lisp之根源 --- 保罗格雷厄姆 来源 http://daiyuwen.freeshell.org/gb/rol/roots_of_lisp.html 约翰麦卡锡于1960年发表了一篇非凡的论文, ...
- 【COGS2479】 HZOI2016—偏序
http://cogs.pro/cogs/problem/problem.php?pid=2479 (题目链接) 题意 四维偏序. Solution CDQ套CDQ. 细节 第二次分治不能直接按照mi ...
- MFC:ID命名和数字约定
今天早上双击一个刚刚编译完成的应用程序,界面刚刚显示,又自动触发了一个菜单事件,打开了一个网页.真的很意外.关闭窗口,再次双击,又自动打开了一个网页,再关闭,再双击,又不自动打开网页了.这是什么情况? ...
- CentOs 自带 PHP 之坑
在虚拟机上安装了CentOs6.5在上面安装了lnmp开发集成包(php7.1),对于之前没有任何开发经验的我来说,正常且安详滴在集成环境上开发着优雅的小bug. 然而我今天在Composer拉取代码 ...
- CNN 中, 1X1卷积核到底有什么作用
转自https://blog.csdn.net/u014114990/article/details/50767786 从NIN 到Googlenet mrsa net 都是用了这个,为什么呢 发现很 ...
- IntelliJ IDEA 破解 - pycharm
MAC激活方法 下载破解文件 下载地址: https://files.cnblogs.com/files/resn/JetbrainsCrack-2.7-release-str.jar.zip 或者去 ...
- 如何用javascript获取和设置css3属性
==================获取======================== 我想到的第一个思路 var test = document.getElementById('test'); c ...
- 关于An internal error occurred during: "Launching MVC on Tomcat 6.x". java.lang.NullPointerException异常处理
一大早上来启动打开myeclipse就报一个这样的错误An internal error occurred during: "Launching MVC on Tomcat 6.x&quo ...
- dialog 菜单实例
dislog 菜单实例 while : do clear menu=`dialog --title system custom` [ $? -eq ] && echo "$m ...