让老板虎躯一震的前端技术,KPI杀手
本文由云+社区发表
作者:思衍Jax
天下武功,唯 (wei) 快(fu) 不(bu) 破(po)。
随着近几年的前端技术的高速发展,越来越多的团队使用 React、Vue 等 SPA 框架作为其主要的技术栈。以 React 应用为例,从性能角度,其最重要的指标可能就是首屏渲染所花费的时间了。那么今天,我们要给大家分享的一个把优化做到极致的故事。
我们的目标是让 H5 的页面也能够拥有 Native 般的体验,如果你还在寻求什么技术能够让老板虎躯一震(拯救你的KPI),那么这篇文章或许能够帮助到你。
企鹅辅导课程详情页是什么
企鹅辅导详情页
课程详情页是腾讯旗下企鹅辅导 APP 中最重要页面之一,也是流量最大的页面之一,所以它的打开速度也是至关重要的。
这是一个使用 React
编写的 H5 页面,运行于多端,包括: 企鹅辅导APP
、手机 QQ
、手机浏览器
。
架构演变
纯异步渲染
我们知道当前主流的 SPA 的应用的默认渲染方式都是这样的:
在这种情况下,从加载页面到用户看到页面(首屏渲染所花费的时间)就是上图中灰色边框区域所包括的时间。
这是最慢的一种方式,就算 CGI 够快,最少要花费 1S 到 2S 左右的时间了。
接着我们简单优化一下:
- 把静态资源缓存起来,这样下次用户打开的时候就不用从网络请求了。
- 第 ④ 步拉取 CGI 这个动作是否可以提前呢?我们可以在请求 HTML 之后,先通过一段 JS 脚本去请求 CGI 数据,后面第 ④ 步的时候,就可以直接拿到数据了,这就是 CGI 预加载。
怎么做到呢?我们的方案是统一封装 Request 请求工具,在用 Webpack 打包的时候,会往页面顶部注入一段 预加载 CGI 的 JS 代码,维护一个CGI 与 DATA 对应 MAP,后面发请求的时候,先去 MAP 里取值,如果有值的话直接拿出来,没有的话则发起HTTP 请求。(具体请查阅我们团队开源的 Preload 工具)
这种模式还有一些其他的优化的方法:
- 在 HTML 内实现 Loading 态或者骨架屏;
- 去掉外联 css;
- 使用动态 polyfill;
- 使用 SplitChunksPlugin 拆分公共代码;
- 正确地使用 Webpack 4.0 的 Tree Shaking;
- 使用动态 import,切分页面代码,减小首屏 JS 体积;
- 编译到 ES2015+,提高代码运行效率,减小体积;
- 使用 lazyload 和 placeholder 提升加载体验。
效果如下图所示:
异步渲染
直出同构
在异步的模式下,除了上述优化,我们还在端内(企鹅辅导 APP、手机 QQ)内做了离线包缓存(腾讯手Q方面独立研发出来的针对手机端优化的方案,简而言之就是将静态资源缓存在手机 APP 内),经过我们的数据测试,首屏渲染大概能够达到秒开(1s左右) 的效果。
-w300
但对有着性能极致追求的我们来说,肯定是不会满意的。
继续优化,最容易、最大众的套路肯定就是直出(服务端渲染)了。
现在直出的方案已经有很多很多种,这里也不多做介绍了,如果您想了解更多关于服务端渲染的方案,请参考这篇文章。
直出针对首屏时间的优化效果是非常明显的,经过我们的测试,数据大概能够提升25%左右。
直出之后的效果如下图:
直出同构
可以看到对于首屏来说,没有了【加载中...】的等待时间,视觉体验提升了不少。
PWA 直出
PWA
针对上述、常见的直出应用来说,我们能够优化的点在哪里呢?让我们来详细分析一波,这也是今天我们要给大家分享的重点。
首先看看直出应用各个环节的耗时表 (本地环境 2018款 iMac):
过程名称 | 过程花费 |
---|---|
Node 内 CGI 拉取 | 300 ms |
RenderToString | 20 ms |
网络耗时 | 10 ms |
前端HTML渲染 | 30 ms |
从上面的表中我们看出,直出渲染的耗时的大头还是在 CGI 接口的拉取上。
我们现在提出两个问题:
- CGI 接口的数据是否可以缓存 ?
- HTML 又是否可以缓存 ?
一、接口的动静分离
动态信息
这个页面的接口数据中,有一些数据,是实时变动的, 比如:当前还剩多少个名额、此时此刻课程的价格、用户是否购买过这个课程等。
这些数据的特性决定了这个数据接口不能够被缓存。(假设将其缓存,那么就会存在可能用户进来看到当前还剩下10个名额,其实课程已经卖光了的情况)
为了这个时间耗时的大头,我们做了CGI接口的动静分离。
将与用户态、当前时间没有关联的数据(比如
课程标题
、课程上课的时间
、试听模块的地址
等)放在一个接口(静态接口),其他变化的数据放在另一个接口(动态接口)。
那么可以使用静态的接口来做服务端渲染,好处是第一比较快(少了动态的信息,而且后台也可以做缓存),第二 Node 直出可以做缓存了。
二、直出 Redis 缓存
这样我们就可以将那部分静态的、不会经常变动的数据用来直出 HTML,然后将这个 HTML 文件缓存到 Redis 中。
客户端请求此网页,Node 端接受到请求之后,先去 Redis 里拿缓存的 HTML,如果 Redis 缓存没有命中,则拉取静态的 CGI 接口渲染出 HTML存入 Redis。
客户端拿到 HTML 之后,会立刻渲染,然后再用 JS 去请求动态的数据,渲染到相应的地方。
做完之后我们可以看到优化效果的提升是非常非常明显的:
直接从 262ms 提升到了 16ms !(本地环境),简直飞一般的感觉,妈妈再也不用担心领导看耗时了。
三、PWA 直出缓存
关于什么是 PWA ,以及如何使用,请移步这篇文章。
做了 Node 端直出的 HTML 缓存之后,我们接着优化,接着思考,是否可以在客户端也缓存 HTML,这样连网络延时这部分消耗也省掉呢。
答案就是使用 PWA 在客户端做离线缓存,将我们直出的 HTML 缓存在客户端,每次用户请求的时候,直接从 PWA 离线缓存里取出对应的直出页面(HTML)响应给用户,响应之后紧接着请求 Node 服务更新本地的 PWA 缓存。(如下图所示)
核心代码:
self.addEventListener("fetch", event => { // TODO other logic (maybe fetch filter)// core logicevent.respondWith(caches.open(cacheName).then(function(cache) {return cache.match(cacheCourseUrl).then(function(response) {var fetchPromise = fetch(cacheCourseUrl).then(function(networkResponse) {if (networkResponse.status === 200) {cache.put(cacheCourseUrl, networkResponse.clone());}return networkResponse;});return response || fetchPromise;});}));
});
废话不多说,先看效果对比 (左 PWA 直出;右 离线包):
duibi
从上图可以看出,使用了 PWA 直出缓存之后,首屏渲染基本是毫秒开,可以说与 Native 并肩了。
经过我们的数据测试,使用 PWA 直出缓存,首屏渲染的时间最好可以到400ms左右级别:
PWA 直出细节优化
一、防页面跳动
因为对接口进行了动静分离,使用静态接口直出页面,然后在客户端拉取动态数据渲染完。这就可能会导致页面的抖动(比如详情页中的试听模块,是在客户端渲染的)。
因为高度改变了,视觉上就会出现抖动(具体可以参考上面章节直出时候的 GIF 截图)。
要去掉页面抖动的情况,就必须保证容器的高度在直出时候已经存在了。
比如这个试听模块,其实这个封面图和试听按钮是可以在服务端渲染出来的,而后面的 Video 模块则必须要在客户度渲染(腾讯云 Tcplayer)。
所以这里可以拆分成:(试听封面 + 按钮 + 时间)服务端渲染 + 底层 Video(客户端渲染)。
有些需要在客户端计算高度的容器(表现为常放在 ComponentDidMount 里计算),如果它们依赖客户端环境(比如依赖当前系统是安卓还是 IOS),就导致他们肯定不能放在服务端直接渲染出来,这又怎么办呢?
这里我们的做法,是将这些计算放在 HTML body 之前,通过内联的脚本嵌入,计算出当前环境,给 body 加上一个特定的类(class),然后在这个特定的类下面的元素,就可以通过 css 给予特定的样式。比如下面代码:
/** 因为在不同的手机 APP 环境内,页面的 padding 是不一样的。* 我们要在页面渲染完之前加上相应的 padding */
var REGEXP_FUDAO_APP = /EducationApp/;
if (typeof navigator !== "undefined" &®EXP_FUDAO_APP.test(navigator.userAgent)
) {if (/Android/i.test(navigator.userAgent)) {document.body.classList.add("androidFudaoApp");} else if (/iPhone|iPad|iPod|iOS/i.test(navigator.userAgent)) {if (window.screen.width === 375 && window.screen.height === 812) {document.body.classList.add("iphoneXFudaoApp");} else {document.body.classList.add("iosFudaoApp");}}
}
.androidFudaoApp .tt {padding-top: 48px;background-position-y: 84px;
}.iphoneXFudaoApp .tt {padding-top: 88px;background-position-y: 124px;
}.iosFudaoApp .tt {padding-top: 64px;background-position-y: 100px;
}
然后把这段代码通过构建插入到页面 body 之前。
-w500
防抖动优化效果如下 (左优化完,右未优化):
duibi_doudong
二、冷启动预加载
虽然我们做了 PWA 离线缓存,但是对于冷启动来说,客户端里面的 PWA 缓存还是没有的,这样就会导致初次点击页面,渲染速度相对慢一点。
这里我们可以在 APP 启动的时候,用一个预加载的脚本最大限度的拉取用户可能访问的页面。
核心代码如下:
// 预加载页面时, PWA 预缓存课程详情页面的直出
function prefetchCache(fetchUrl) {fetch("https://you preFetch Cgi").then(data => {return data.json();}).then(res => {const { courseInfo = [] } = res.result || {};courseInfo.forEach(item => {if (item.cid) {caches.open(cacheName).then(function(cache) {fetch(`${courseURL}?course_id=${item.cid}`).then(function(networkResponse) {if (networkResponse.status === 200) {cache.put(`${courseURL}?course_id=${item.cid}`,networkResponse.clone());}// return networkResponse;});});}});}).catch(err => {// To monitor err});
}
PWA 直出遗留问题
一、兼容性问题
随着 PWA 技术的发展,现今大部分手机以及 PC 环境已经支持对 PWA 进行了支持。经过我们的测试发现:安卓基本上都是支持的,IOS 需要11.3以上才支持。
Service Workers 兼容性图
二、IOS 渲染问题
很多的经验告诉我们,外联的 script 标签要放在 body 的后面,因为它会阻塞页面的 DOM 渲染。
经过测试发现,IOS 的 WebView
(UIWebView
)渲染机制并不会上述一样,而是要等到后面的 JS 执行完之后才渲染页面,如果是这样,我们的直出渲染优化就没有效果了(因为 HTML 并不在最开始渲染),这里可以使用 script
标签的 async
与 defer
属性来达到异步渲染的作用。
升级 WkWebView 之后,情况得到改善,渲染正常。
附录
参考资料
- PWA 的探索与最佳实践
- 亿万级访问量下的前端同构直出实践
- React 16 加载性能优化指南
此文已由作者授权腾讯云+社区在各渠道发布
原文:企鹅辅导课程详情页毫秒开的秘密 - PWA 直出
获取更多新鲜技术干货,可以关注我们腾讯云技术社区-云加社区官方号及知乎机构号
让老板虎躯一震的前端技术,KPI杀手相关推荐
- 【CSS】虎躯一震!flex=1 和 flex-grow=1 竟然不一样!
2019独角兽企业重金招聘Python工程师标准>>> 在以前做项目的时候,使用 flex,而且当遇到 子元素 需要 占满剩余空间 的时候,习惯性的会添加 flex=1 这个属性. ...
- 尤雨溪携手字节前端专家,畅聊 Vue 3.0 前端技术新趋势
前端这个技术领域,在应用化以后,涵盖的内容越来越广--纯表现层.应用实现层.应用架构层.基础设施层到改进开发范式的理念层,都有太多可以去钻研的技术点,衍生出了无数前端开发的发展路线."别更新 ...
- 前端技术周刊 2018-12-03:DOM
前端快爆 Chrome 71 开始将试用 SXG 功能,它是由 IETF 提出,Web Package 协议规范下的 Signed HTTP Exchanges 功能的缩写.该技术使得一个第三方服务器 ...
- 2019年一半已过,这些大前端技术你都GET了吗?- 下篇
在上一篇文章中已经介绍了大前端关于状态管理.UI组件.小程序.跨平台和框架层的内容.在本文中,我会继续介绍编程语言.工程化.监控.测试和服务端,同时也会对下半年大前端可以关注的部分进行展望. 结合个人 ...
- 前端技术周刊 2019-02-11 Serverless
前端技术周刊 2019-02-11 前端快爆 Chrome 72 的安卓版正式启用了 Trusted Web Activity 功能,可以使得一个 PWA 应用能够在 Google Play Stor ...
- 具有先天开源优势的Web前端技术社区里也是波澜不惊
让人感觉奇怪的是,在中文社区始终未见这种学院派.甚至在具有先天开源优势的Web前端技术社区里也是波澜不惊,可见写一篇好的技术文案真的比登天还难.我所见到的大部分所谓文档索性把代码里输出数据的语句块拷贝 ...
- 前端系列 | 2015年双11手淘前端技术巡演 - 前言
前端系列 | 2015年双11手淘前端技术巡演 - 前言 2015-11-16 手机淘宝技术团队MTT 15年双11刚落下帷幕.今年众所周知,是全面"无线化"的一年.数据上我就不说 ...
- 前端技术周刊 2018-10-08:WebGL
前端技术周刊 2018-10-08 大家好,有半个月没见了.最近编辑部忙到爆炸,出刊节奏被打乱,预计在今年双十一之后恢复正常出刊速度. 前端快爆 10月5日,Google 发布 Project Str ...
- 跟着大咖学前端(国内知名前端技术汇总)
国内知名前端大佬汇总,该名单排名 不分先后 尤雨溪(尤小右)- vue项目作者 相关采访:https://www.jianshu.com/p/3092b382ee80 微博:https://weibo ...
- 前端技术层出不穷,敢问路在何方?
前端技术层出不穷,敢问路在何方? 回想2012年还是jquery的天下,后来2015年vue.react开始盛行,到现在的红的发紫也才不过3年光景,当然vue生态圈的nuxt,react的next ...
最新文章
- 大脑天天超负荷,三分天赋,七分练,世间惊现普通脑修炼秘籍
- [linux内核]將ubuntu更新到最新的内核
- arcgis api for flex 开发入门(九)webservices 的使用
- shell编程中crontab用法超级详解!
- Nordic nRF5 SDK和softdevice介绍
- 重建控制文件的案例(RESETLOGS模式和NORESETLOGS模式)
- Thymeleaf的Spring数据
- 2017.6.4 problem b 失败总结
- 品方牌可以利用视号频做什么
- 导入php插件_漏洞分析丨WordPress评论插件wpDiscuz任意文件上传
- php preg_split 正则截取字符串
- 【Web技术】771- 图片懒加载从简单到复杂
- oracle64位 32位plsql,64位oracle 安装32位plsql develop
- 计算机更换固态硬盘方法,笔记本电脑固态硬盘如何更换
- 用stream流将list集合根据某个字段分组成Map<String,List<T>>类型的集合
- 英语计算机手抄报图片大全,五年级英语手抄报图片大全
- 创建个人网页,创建个人网址。
- 单利模式的优缺点和使用场景
- 2703 奶牛代理商 XII
- POI导入导出Excel数据(IDEA版)简单运用