最近两个项目同时开发,使用了Vue2的SSR,这样后端渲染页面首屏能够加快页面呈现,增长SEO和用户体验,可是项目上线后却发现了严重的性能问题,因而在三天内两次重大调整,最后只能放弃Vue SSR,本文从Vue SSR实现开始,逐渐复盘整个事件。
两周前就预告了要写一篇Vue SSR的文章,可是没想到上周四上线以后,周六放量以后发现性能问题,这周一到周三,作了两次重大调整,最终仍是放弃了SSR,而且作了此次事件复盘。html
调研Vue已经好久了,随着Vue2正式发布,使用Vue来作项目又燃起了但愿,不是为了一时的技术理想和情怀(了解个人人都知道,我不是这样的人),主要是出于下面几方面考虑:前端
因此,最后决定:上Vue!技术栈:Vue2+Yog2。vue
再介绍下两个项目:webpack
先看下Vue SSR的实现流程图:
web
简单解释一下:ajax
可是这张图没有说明在调用API接口方面,先后端是怎么公用代码的。前端走的是Ajax请求,后端走的是http请求(百度内部是RAL接口服务管理),结合上图补充完整的代码执行流程图以下:
vuex
在浏览器内使用ajax请求,而在服务端须要调用内部API请求或者直接读取存储(RAL)。ajax请求到达服务端依次通过:action层、model层,最后走到仍是API请求或者读取数据(这里重点读三遍。。)。json
这里咱们将服务端和客户端API的请求方法写在不一样的文件内,可是封装暴漏的接口都是同样的(接口模式)。在webpack里面,针对server和client提供不一样的alias:
后端
这样 require('api/demo')
的时候,会区分开server和client。api
server内直接使用yog2 modal内的获取数据方法,好比:
而client内,直接使用ajax请求:
即下图的流程:
在渲染的时候,prefetch阶段经过dispatch触发Store的Action(Action内容许异步),Action内调用 api/demo
获取数据成功后commit mutation,这样整个数据就跑通了。
server.js是第一次渲染使用的入口action,核心代码以下:
//vue2.2
const vueServerRender = require('vue-server-renderer');
const bundle = require('../vue-ssr-bundle.json');
const renderer = vueServerRender.createBundleRenderer(bundle, {
template: '<!--vue-ssr-outlet-->',
cache: require('lru-cache')({
max: 1000,
maxAge: 1000 * 60 * 15
})
})
// 先渲染tpl(swig模板),内容相似vue ssr demo的index.html
// 这里渲染使用chunk,先输出不依赖数据的头部html
res.render('page/index.tpl', { isSendSpeedCode }, (err, html) => {
if (!err) {
var htmls = html.split('<!--vue-ssr-outlet-->')
//先渲染头部html
res.write(htmls[0])
// swig渲染时间
var time1 = Date.now()
const renderStream = renderer.renderToStream(context)
renderStream.on('data', chunk => {
// 边解析,边渲染html
res.write(chunk)
})
renderStream.on('end', () => {
if (isSendSpeedCode) {
// 统计vue 渲染时间
var time2 = Date.now()
var code = ` <script>if(window.alog){ alog('speed.set', 'p_swig', ${time1 - time0}); alog('speed.set', 'p_vue', ${time2 - time1}); }</script> `
res.write(code)
}
// 渲染尾部html
res.end(htmls[1])
}).on('error', errorHandler)
} else {
errorHandler(err)
}
})复制代码
webpack是vue「全家桶」的后遗症,项目太急没办法去掉。咱们项目的目录结构以下:
项目须要两次打包:
vue-src
文件夹内容根据 server-entry
和 client-entry
打包出来,分别放进yog2的client和server对应的文件,以后 vue-src
在执行环境就不须要了这里有个细节:webpack打包出来的静态资源路径须要跟FIS3打包的静态资源路径一致,否则就无法经过FIS3进行静态资源定位,好比替换为CDN地址。
因为vue2.2打出来的server-bundle是json格式文件,因此FIS没法将json内的静态资源进行统一管理,须要webpack判断生产环境直接替换为CDN地址
手百的通用库Bdbox是client代码,代码中有一些window
全局变量的使用,而咱们知道Node是没有 window
的,在Node执行SSR的时候,会报错,好比下面的代码:
// 自执行
isAndroid: /(Android);?[\s\/]+([\d.]+)?/.test(navigator.userAgent)复制代码
有两种改法:
.isAndroid
由属性变成方法:.isAndroid()
,放到mount
内执行navigator.userAgent
的context目录结构深了,尤为是Vue里面还须要调用yog model的代码,会各类../../
很蛋疼,能够利用alias简化写法:
须要注意的是static
的写法是:<img src=“~static/img/logo.png”
订好接口请求参数和返回数据格式以后,后端RD进行API的编写同时,咱们能够利用Yog2的Mock功能,对ral返回的数据进行假数据测试,实现后端和前端RD解耦,大大提升开发效率。
如今来复盘下整个事件:
从周一到周三通过两次大的调整,终于服务稳定了,其中代码review阶段,咱们也发现了不少代码不规范的现象。下面来讲下咱们使用vue ssr的一些压测等数据。
从上线以后,内存积累到必定时间就飙升,内存飙升同时,CPU也进行飙升,具体曲线以下:
从12日(周三19点)上线以后,就开始平稳了,13日中午缩容后,CPU稍有上扬。
同期QPS的数据以下:
查看全文,关注微信公众号:「三水清」(sanshuiqing123)