全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

详解如何使用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。由于接口数据经常发生变化,所以没有做缓存。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# vue服务端渲染  # vue.js  # 2.0服务端渲染  # vue2  # 服务端渲染  # 详解vue服务端渲染(SSR)初探  # 基于vue-ssr服务端渲染入门详解  # 详解基于vue的服务端渲染框架NUXT  # vue服务端渲染的实例代码  # 详解vue服务端渲染浏览器端缓存(keep-alive)  # vue ssr服务端渲染(小白解惑)  # vue服务端渲染添加缓存的方法  # vue的ssr服务端渲染示例详解  # Vue3+TypeScript+Vite服务端渲染项目的实现  # 服务端  # 客户端  # 的是  # 会将  # 重试  # 加载  # 是在  # 还可以  # 过了  # 将会  # 上了  # 有很多  # 走到  # 我一  # 一个月  # 我来  # 之家  # 推荐使用  # 并在  # 将其 


相关文章: 装修招标网站设计制作流程,装修招标流程?  制作表格网站有哪些,线上表格怎么弄?  网站制作价目表怎么做,珍爱网婚介费用多少?  武汉网站如何制作,黄黄高铁武穴北站途经哪些村庄?  如何注册花生壳免费域名并搭建个人网站?  如何制作新型网站程序文件,新型止水鱼鳞网要拆除吗?  小捣蛋自助建站系统:数据分析与安全设置双核驱动网站优化  宁波自助建站系统如何快速打造专业企业网站?  学校为何禁止电信移动建设网站?  湖州网站制作公司有哪些,浙江中蓝新能源公司官网?  如何通过建站之星自助学习解决操作问题?  详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)  如何在香港免费服务器上快速搭建网站?  建站之星下载版如何获取与安装?  如何在腾讯云服务器上快速搭建个人网站?  北京专业网站制作设计师招聘,北京白云观官方网站?  如何在Mac上搭建Golang开发环境_使用Homebrew安装和管理Go版本  网站制作难吗安全吗,做一个网站需要多久时间?  如何快速完成中国万网建站详细流程?  网站制作知乎推荐,想做自己的网站用什么工具比较好?  网站制作公司排行榜,抖音怎样做个人官方网站  如何在景安云服务器上绑定域名并配置虚拟主机?  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  如何高效搭建专业期货交易平台网站?  如何通过服务器快速搭建网站?完整步骤解析  详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)  北京制作网站的公司,北京铁路集团官方网站?  免费的流程图制作网站有哪些,2025年教师初级职称申报网上流程?  北京网站制作公司哪家好一点,北京租房网站有哪些?  建站之星与建站宝盒如何选择最佳方案?  C#怎么使用委托和事件 C# delegate与event编程方法  如何在阿里云服务器自主搭建网站?  在线ppt制作网站有哪些,请推荐几个好的课件下载的网站?  临沂网站制作公司有哪些,临沂第四中学官网?  义乌企业网站制作公司,请问义乌比较好的批发小商品的网站是什么?  建设网站制作价格,怎样建立自己的公司网站?  如何安全更换建站之星模板并保留数据?  建站之星安装后如何配置SEO及设计样式?  利用JavaScript实现拖拽改变元素大小  如何选择高效可靠的多用户建站源码资源?  无锡营销型网站制作公司,无锡网选车牌流程?  网站设计制作书签怎么做,怎样将网页添加到书签/主页书签/桌面?  品牌网站制作公司有哪些,买正品品牌一般去哪个网站买?  如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?  简单实现Android文件上传  如何通过虚拟主机快速完成网站搭建?  开源网站制作软件,开源网站什么意思?  天河区网站制作公司,广州天河区如何办理身份证?需要什么资料有预约的网站吗?  免费制作小说封面的网站有哪些,怎么接网站批量的封面单?  公司网站的制作公司,企业网站制作基本流程有哪些? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。