大家好,我是若川(点这里加我微信 ruochuan12,长期交流学习)。今天推荐这篇图文并茂的SSR技术文章。这是江西前端群里一个小伙伴的文章。群里小伙伴很多都在知名大厂,但他们都很低调。

点击下方卡片关注我、加个星标,或者查看源码等系列文章。学习源码整体架构系列、年度总结、JS基础系列


SSR最佳实践

秒开率对于用户的留存率有直接的影响,数据表明, 网页加载时间过长会直接导致用户流失.转转集团作为一家电商公司, 对于H5页面的秒开率有着更加严格的需求, 在主要的卖场侧页面(手机频道页、3c频道页、活动页)等重要流量入口我们都采用了SSR(服务端渲染)技术来构建页面,今天就带大家了解一下我们摸索出来的一些最佳实践.

网页的前世今生

在早期的web应用中,实际上我们都是用的服务端渲染技术, 像jsp、asp、php等各种后台模板生成的页面,前端都是拿到整张页面,不用自己去拼接DOM.后来随着前后端分离开发模式,衍生出了最主要的两种渲染方式CSR以及SSR.

  • CSR : 客户端渲染,整个渲染流程是:  浏览器请求url --> 服务器返回index.html(空body、白屏) --> 再次请求bundle.js、路由分析 --> 浏览器渲染bundle.js体积越大, 就会导致白屏的时间越长,给用户的体验就越差(当然,这可以借助打包构建工具来优化这部分)

    交互流程图如下

图1-1客户端渲染流程图

  • SSR : 服务端渲染,  服务端渲染分为两个步骤:

  • 阶段一:  浏览器请求url --> 服务器路由分析、执行渲染 --> 服务器返回index.html(实时渲染的内容,字符串) --> 浏览器渲染  (此时是一个静态页面, 不可交互, 依赖于服务端的能力, 这就是快的原因)

  • 阶段二:浏览器请求bundle.js --> 服务器返回bundle.js --> 浏览器路由分析、生成虚拟DOM --> 比较DOM变化、绑定事件 --> 二次渲染 (用户可交互)

我们来看看整个交互流程图 :

图 1-2 服务端渲染交互流程图

SSR构建逻辑

理解了两种渲染模式的异同,我们来看看SSR整个构建逻辑(主要以Vue-SSR为例)

我们以官网的图片为例:

图 2-1 SSR构建逻辑

从图中我们可以知道 :

在整个构建过程中, 我们有两个入口, 一个是server-entry.js, 执行server端的逻辑, 一个是client.js, 执行client端的逻辑, 然后通过会将webpack打包分成两个Bundle: 服务端bundle; 客户端bundle. Node.js会处理服务端bundle用于SSR, 客户端bundle会在用户请求时和已经由SSR渲染出的页面一起返回给用户, 然后在浏览器执行”注水”(hydrate), 接管Vue接下来的业务逻辑.

理解了整个构建逻辑,接下来我们来看看我们是怎么运用SSR来服务我们的项目的.

SSR构建项目的背景

卖场侧业务首页组成大同小异: 主要分首屏和第二屏, 首屏有多个模块组成, 第二屏是商品Feed流,便于读者理解, 我们抽象出了页面结构图:

图 2-2 页面结构图

(欢迎大家下载转转app体验)

而且这些页面都有一个共同的特点:

  • 只用于展示, 和用户的状态不强绑定(不需要用户登录)

  • 页面状态稳定,内容不会经常变更

  • 都是重要流量的第一个入口(首页)

由于对于秒开率有着极高的要求,又承载了主要流量入口,结合以上页面特点,所以我们使用了SSR来提升用户体验.

经过一系列的探索和探究, 我们最终使用Nuxt.js来作为我们的技术选型.

这里提下为啥使用Nuxt.js作为我们的技术选型, 主要原因有以下几点:

  1. 集团内部C端业务是以Vue技术栈为主,B端技术栈是以React为主, 所以不考虑React服务端渲染技术栈;

  2. Nuxt.js是开箱即用的服务端渲染框架,不用开发人员自己去搭建Vue+ Vue-server-renderer + vuex来集成服务端渲染框架, 接入成本比较低.

SSR运用的最佳实践

目前我们使用SSR实现的主要能力有:

  • 首屏使用服务端渲染,第二屏使用客户端渲染,首屏模块数据可调整,即优化了性能, 又丰富了页面配置.

  • 合理化使用缓存, 进一步提升用户体验.

  • 实现css注入,达到按需换肤的效果.

  • 使用ErrorBoundary拦截错误,使得组件错误不会影响整个页面白屏.

  • 按需加载第二屏数据,只有滑动到可见范围, 才加载第二屏数据

  • 针对大促场景, 结合服务端能力, 以及各种监控,docker扩容,保证页面稳定.

接下来就和大家探索其中几种能力的主要思路:

怎样实现首屏使用服务端渲染,第二屏使用客户端渲染

这种实现方式主要是结合asyncData在服务端异步获取数据,使用vue动态组件component的特性,来调整模块的渲染顺序; mounted生命钩子只会在客户端执行, 使用仅在客户端渲染组件的特性来实现的.

示例代码:

<template><!--服务端渲染,动态获取首屏模块并且加载对应模块的数据, 使用error-boundary来拦截错误--><template v-for="(e, i) in structureOrder"><error-boundary><component :info="activityState.structure[e]":is="Mutations.name2Component(e)"class="anchor":id="e":key="i" :name="e" v-if="activityState.structure[e] || e === 'bar' "/></error-boundary></template><!--客户端渲染--><client-only><!--滑动到可见范围加载对应的数据--><div :is="listComponent" :tab="labelFilter"/></client-only>
</template>

获取数据:

//服务端渲染数据
async asyncData({app, route, req}) {const initData = await app.$axios.$get(host, {params: {name: key, from, smark, keys: `structure,base,labelFilter,navigate,redPack,${elements}`}, headers})const {structureInfo, structureOrder, restStructure, anchors} = Mutations.initStructure(initData)return {structureInfo,restStructure,structureOrder, //动态返回对应模块的名称useVideo: Mutations.checkUseVideo(req),theme,pageFrom: route.query.from,isPOP,anchors,...formInfo}
},
async mounted() {//获取客户端渲染的数据const res = await this.initData()
},

怎样使用ErrorBoundary捕获组件级别错误,避免整个页面白屏

关于ErrorBoundary这个捕获错误的组件,这个组件的主要功能是使得组件级的错误不会蔓延到页面级,不会造成整个页面的白屏,考虑到服务端渲染可能会发生偶发性错误,状态容易变的不可控, 所以使用这个能力还是很有必要的, 这个组件主要使用vue提供的 errorCaptured 来捕获组件级的错误, 想详细了解这个api的作用可以去看官方文档,具体的实现如下:

const errorBoundary = Vue => {Vue.component('ErrorBoundary', {data: () => ({ error: null }),errorCaptured(err, vm, info) {this.error = `${err.stack}\n\nfound in ${info} of component`SentryCapture(err, 1) //异常上报到sentryreturn false},render() {return (this.$slots.default || [null])[0] || null}})
}// 全局注册errorBoundary
Vue.use(errorBoundary)

怎样实现css注入,实现页面换肤

这个功能的主要作用是 : 可以根据配置json文件定制化活动页面的样式, 做到"千人千面" (一个会场的key可以配置一种样式, 但是底层代码是一套),使得元素多样化,在视觉上给用户体验带来很大提升.

我们先来看看效果示意图:

以上就是展示效果, 借住CSS注入, 我们可以根据不同的json文件来定制化页面的样式, 只需要维护一套代码, 简单高效.

实现逻辑也很简单,主要是运用了Nuxt.js框架提供的head方法:

head() {//this.baseInfo.additionStyle是从json文件拿到的样式//通过css权重, 可以实现样式覆盖return {style: [{cssText: this.baseInfo?.additionStyle || '', type: 'text/css'}],__dangerouslyDisableSanitizers: ['style']  // 防止对一些选择器的特殊字符进行转义}},

不仅如此, 还可以实现js注入, 感兴趣的小伙伴可以自己去了解,底层原理可以了解下 vue-meta 这个库

但是, 随着业务的不断迭代, 这种注入方式还是存在很多可优化的点:

  • 每个活动页运营同学都要维护一份json文件,里面包含冗长的css配置字段, 活动规则等等, 特别是css配置字段,就是一段冗长的css, 给运营和开发同学带来很大的不便;

  • 运营同学维护成本高,学习成本高,操作成本高;

  • 对于UI同学成本也大, 每次都需要UI同学来设计活动页面样式;

目前, 集团内部正在使用 魔方 一步一步去替代这种方式, 魔方 只需要运营同学拖拖拽拽, 就能生成一个活动页, 简单高效, 想要了解魔方的同学, 可以继续关注我们的公众号

怎样实现组件滑动到可见范围,才加载数据

其实这种优化页面的方法并不是说只适用于SSR, 其他非SSR页面也可以使用这种方式来优化;

看看我们的实现方式 :

function asyncComponent({componentFactory, loading = 'div', loadingData = 'loading', errorComponent, rootMargin = '0px',retry= 2}) {let resolveComponent;return () => ({component: new Promise(resolve => resolveComponent = resolve),loading: {mounted() {const observer = new InterpObserver(([entries]) => {if (!entries.isIntersecting) return;observer.unobserve(this.$el);let p = Promise.reject();for (let i = 0; i < retry; i++) {p = p.catch(componentFactory);}p.then(resolveComponent).catch(e => console.error(e));}, {root: null,rootMargin,threshold: [0]});observer.observe(this.$el);},render(h) {return h(loading, loadingData);},},error: errorComponent,delay: 200});
}export default {install: (Vue, option) => {Vue.prototype.$loadComponent = componentFactory => {return asyncComponent(Object.assign(option, {componentFactory}))}}
}

实现原理主要是使用vue高阶组件, 元素到达可见范围内, 延迟加载组件;

看看效果图:

我们可以看到,只有到底部商品Feed流出现在可视范围,才去请求对应的接口

针对大促场景怎样保证页面稳定

所谓大促场景,是指像 6.18, 双11,这种场景下, 面对大流量, 如何保证页面稳定? SSR是CPU密集型任务, 意味着很耗费服务器资源,集团目前主要采取的策略是:

  • 对接口进行压测, 模拟高并发场景下页面性能, 接口响应速度;

  • 集团内部实现了一套监控系统, 可以实时监控CPU,内存的消耗情况;

  • 需要有服务器扩容方案, 比如接入docker, 可以实现服务器实时扩容

怎样利用缓存

请大家移步集团一位前端大佬写的公众号文章: Nuxt实现的SSR页面性能优化的进一步探索与实践

最终,看看我们最终的实现效果:

可以看到, 首屏渲染时间在594ms, 秒开率在百分之87左右;

SSR的不足

ssr的使用过程并不是一帆风顺的, 在使用的过程中, 也总结几点不足之处:

  • 对于开发人员的要求更高, 要学习其他的额外知识,例如: Linux , node相关知识, 需要具备一定的后端思维;

  • 服务端渲染接口抓包不方便, 我们在客户端抓取不到服务端的接口请求, 不过对于使用Mac电脑开发的同学,可以使用 proxychains-ng 来抓取服务端请求的接口

  • 冗长的配置环境过程, 每次开发联调需要配置后端host

  • 对于服务器资源有要求,并发量越大, 资源消耗的越多

  • 服务端渲染可能会发生偶发性错误, 需要有一套降级方案

至于如何取舍, 看各位同学的项目需求,以及运用场景;

总结

SSR的使用有利有弊, 我们应该结合自己的业务特性去制定合适的方案, 它的优点就是快, 有利于SEO, 缺点也很明显, 比较耗费服务器资源, 对于亿级流量的超巨app来说, 理论上是不太合适的, 集团内部也有自己的一套方案来优化客户端渲染, 使得用户体验尽量向SSR靠齐.每一种技术的运用只有实践了才知道利弊,才能产生碰撞. 本文只是简单的带大家了解一条业务线上对SSR的运用, 所阐述的方面也只是冰山一角, 希望给广大开发者带来一定的启发, 前人栽树, 后人乘凉, 感谢转转FE前辈们留下的宝贵财富.

参考资料

nuxt官网:  https://www.baidu.com/link?url=xy0d8KPUgTmiVoGge6g-FgdeqjJSTjxdpT0tpxZzBG_&wd=&eqid=db50cacf00052e5e000000066081587d

Vue SSR指南: https://ssr.vuejs.org/zh/guide/


最近组建了一个江西人的前端交流群,如果你也是江西人可以加我微信 ruochuan12 拉你进群。


················· 若川出品 ·················

今日话题

写篇原创优质文章不容易,写了文章特别希望让更多人看到,写过文章的都懂。就像我现在每篇文章都带上源码系列链接,也是希望更多人看到。江西前端群里小伙伴写了这篇文章,我连忙联系开了白名单转载。公众号平台保护原创作者权益非常好,如果你看到一篇文章被转载多次,大概率说明这篇文章确实不错。欢迎分享、收藏、点赞、在看我的公众号文章~

一个愿景是帮助5年内前端人走向前列的公众号

可加我个人微信 ruochuan12,长期交流学习

推荐阅读

我在阿里招前端,我该怎么帮你?(现在还能加我进模拟面试群)

若川知乎问答:2年前端经验,做的项目没什么技术含量,怎么办?

点击方卡片关注我、加个星标,或者查看源码等系列文章。
学习源码整体架构系列、年度总结、JS基础系列

能让你纵享丝滑的SSR技术,转转这样实践相关推荐

  1. typora+picgo+gitee搭建免费图床纵享丝滑

    typora+picgo+gitee搭建免费图床纵享丝滑 0.写在前面 由于之前自己在github搭了自己的博客,伴随之而来的一系列问题.如github不显示图片,github图床加载太慢等一系列问题 ...

  2. 纵享丝滑滑动切换的周月日历,可流畅滑动高度定制,仿小米日历,基于 material-calendarview

    monthweekmaterialcalendarview 项目地址:idic779/monthweekmaterialcalendarview  简介:纵享丝滑滑动切换的周月日历,可流畅滑动高度定制 ...

  3. 让你的小米5纵享丝滑,可能是最快的小米5rom

    小米5刷原生7.1.1 ROM.配合xposed框架模块,非常的流畅!用纵享丝滑形容真不为过. 此标题是根据我的多次刷机实践得来的结果.话不多说先上几张GIF. 下面我上资源和教程: 首先手机要解锁刷 ...

  4. android 涂鸦 卡顿,涂鸦框架的优化——解决绘制时的卡顿问题,纵享丝滑

    前言 V5.5: 增加优化绘制的选项,可优化绘制速度和性能,纵享丝滑. boolean optimizeDrawing = true; // 是否优化绘制,建议开启,可优化绘制速度和性能. Doodl ...

  5. 阿里云数据库专家白宸:Redis带你尽享丝滑!(图灵访谈)

    访谈嘉宾: 本名郑明杭,现阿里云NoSQL数据库技术专家.先后从事Tair分布式系统.Memcached云服务及阿里云Redis数据库云服务开发,关注分布式系统及NoSQL存储技术前沿. 作为嘉宾,曾 ...

  6. 高通CVPR神研究:视频处理计算量降低78%,教卷积层自己“挑像素”,卡成PPT的视频纵享丝滑...

    萧箫 发自 凹非寺 量子位 报道 | 公众号 QbitAI 图像领域,已经限制不住AI算法大牛们的身手了. 现在,随着视频产业火热发展,相关算法也正成为计算机视觉研究的新潮流. 毕竟日常生活中,无论是 ...

  7. 基于 高德 + Windvane 的H5选址工具,纵享丝滑,对高德选址组件说:走你

    一.背景 1.1 需求 **多个产品和业务方反应:**高德的选址组件不好用,跟demo一样,能不能换一个? 秉承着,作为一名即将优秀的程序员不能 Say NO 的原则,我接了下来 1.2 现有方案缺失 ...

  8. windows11视频及网盘资源,纵享丝滑!

    ​视频来自B站翼王,笔者亲自体验,操作非常丝滑! 微信扫码或搜索公众号"程序员阿俊",回复"win11",开启丝滑模式!

  9. 【达摩院OpenVI】几行代码,尽享丝滑视频观感

    团队模型.论文.博文.直播合集,点击此处浏览 随着网络电视.手机等新媒体领域的快速发展,用户对于观看视频质量的要求也越来越高.当前市面上所广为传播的视频帧率大多仍然处于20-30fps,已经无法满足用 ...

最新文章

  1. Redis进阶实践之三如何在Windows系统上安装安装Redis
  2. 56.4 AP!超越YOLOv4,更快更强的CenterNet2来了!
  3. Servicehot和你说说运维自动化的那些事儿
  4. [BZOJ2716/2648][Violet 3]天使玩偶/SJY摆棋子[KDtree]
  5. python3 shell 正则表达式 攫取复杂字符串特定子串
  6. 面向对象程序设计(OOP)的六大原则
  7. 参数估计_随机微分方程的参数估计(一)
  8. 前谷歌技术主管亲述:程序员请避开这几大雷区
  9. 如何获取e.printStackTrace()的内容
  10. 首届全国信创大赛圆满收官,信创新势力载誉而归!
  11. 中国移动MM如何助力网络视频反盗版
  12. 组件用.vue还是.js_适用于qart.js的vue 2.x的组件
  13. 厉害了!北大3位硕博生搞出ChatGPT版Excel!动动嘴就能自动处理表格……免费用!...
  14. 虚幻蓝图实现只狼的钩索系统(2)-射线检测
  15. C++中使用ODBC连接SQL Server数据库
  16. FRM笔记之金融市场与产品-关于利率
  17. pygame演示----黑猩猩小游戏
  18. Window Internal 读书笔记
  19. 英文字母html,利用HTML5实现英文字母ABCD动画特效
  20. UE4纯蓝图-生存开发游戏

热门文章

  1. php smarty框架案例,php封装的smarty类案例
  2. 使用sphinx快速为你python注释生成API文档
  3. JavaScript 之 动态加载JS代码或JS文件
  4. socket与socketServer通信
  5. atmega8 例程:T1定时器 CTC模式 方波输出
  6. oracle数据库扩展目录,Oracle 扩充磁盘空间
  7. python临床数据_从临床试验中获取数据
  8. 移动端页面不需要在meta加_移动端适配viewport缩放方案
  9. 可做fft分析吗_小吃店生意好做吗,小吃业行情分析
  10. Collections.min()和Collections.max()的使用