因为降本增效,我也在互联网圈激流勇退了,拿上亲爱的大礼包,高兴的告别同事,开始规划自己的下一段工作。
因为学历(普通本科)和不想刷八股的问题,工作年限也比较长了,大厂管理我是不太敢奢望了,但是久居人下亦非我所愿,索性就成立了一家公司 ------- 微谏科技。
公司成立好了,我也不能吃老本呀,所以就在接单平台(某客栈)逛了逛(非广告,体验不做评价,等我再用一段时间)。就碰见了我的第一位用户,他的诉求是页面目前加载要4, 5s,希望我能优化到2s内。
性能优化,这个话题老生常谈了,很多资料里对资源的压缩,请求的合并,懒加载,重复渲染的优化做了很详尽的描述,各种 performance 的差值计算,lighthouse 等监测工具的使用让人眼花缭乱,
初入门径的开发者跟着一顿操作,发现居然观感差不多。我之前在团队里也做过一段时间性能优化,简而言之,如果项目代码的写法没明显的性能问题,光靠资源压缩,加载,可能能让指标上去一点,但是让人的整体观感并不会有质的飞跃。
那本次,我是否能幸运的让项目性能飞跃起来呢,并得到用户支付的佣金呢,让我们开始正文吧!
1. 结构分析
我欣然接受后,打开了项目,项目如下。
jsp
<html>
<@include "path/commonJS.jsp" @>
<@include "path/commonUploadJS.jsp" @>
<body>
<div>内容</div>
<iframe src="path/iframe1.jsp" ></iframe>
<iframe src="path/iframe2.jsp" ></iframe>
<iframe src="path/iframe3.jsp" ></iframe>
<iframe src="path/viewer.html" ></iframe>
<script>
// 各种变量定义;
var a,b,c;
// 各种ajax请求
$.ajax(...);
</script>
</body>
</html>
2. iframe 懒加载
这个结构是从几千行的 jsp
代码中简化而来,咱们要做优化,主打的就是对一个页面的了解,看到简化后的页面,里面有多个 iframe
,iframe
会带来以下可能的性能问题。
- 多个
iframe
会相互抢占下载资源 iframe
的js
阻塞会影响父界面
的展示
在分析了 iframe
的作用后发现,有两个 iframe
在首屏是隐藏的,需要通过切换页面中的 Tab
才会将 display
切换为 block
。所以这两位漏网之鱼就可以使用 iframe
的 loading
属性一网打尽。这样这两个 iframe
既不会抢占下载资源,也不会占用解析 html
,执行 js
的时间。
html
<iframe loading="lazy" src="path/iframeHidden" style="display:none"></iframe>
3. 优化白屏时间
解决上一个显而易见的问题后,让我们来关注下首屏时间,从上方示例可以看到,父页面首屏是有静态内容的,但是从实际情况看,要到一些 ajax
请求完成后,才能显示出来,这违背了我们的认知,ajax
不应该阻塞页面的渲染。
好在这些 ajax
都是在一个名为 init
的方法中执行的,我想着那就加一个 setTimeout
吧,这样肯定能让中间有空闲的时间,这样浏览器就会抓住机会渲染首屏了。
不出所料,当我把 setTimeout
放到 500ms
后,首屏确实提前渲染出来了。那小伙伴们肯定就有疑问了,为什么是 500ms
而不是 0ms
,怎么去界定这个时间呢。
究其原因,是有一段框架的解析时间花了 400ms
左右,他是在 script
里直接执行的,且所有 script
都有部分写法可能依赖这个框架的解析。对于一个历史大项目来说,框架的写法无法去做改动,影响面太大,暂不做讨论。
如果说咱们 setTimeout
放到 200ms
,框架解析完就会立马执行这个 setTimeout
,浏览器还是没有得到喘息的机会,白屏时间还是很长。
但是这个方法有一个漏洞,就是调试的电脑,框架平均解析时间是 400ms
,有没有可能别的电脑很快,200ms
就做完了,那就白白浪费了 300ms
。接着我想到了一个好办法 requestIdleCallback
。这样我们就不用管到底该等多久了,浏览器自己会去处理好的。
4. ajax 还能阻塞页面?
上面的方式取巧的解决了首屏的渲染问题,但始终有一团疑问在心头,ajax
为什么会阻塞页面渲染,仔细看了此处代码后恍然大悟。
js
function getData() {
var data;
$.ajax({
url: '/path/api',
async: false,
onSuccess: function(res) {
data = res;
}
});
return data;
}
var a = getData();
注意看,这里用到了一个属性 async
值为 false
,这就是阻塞页面的元凶,它会让我们创建的 xhr
不太机灵。从 MDN
上也有相关的描述。
由于客户此处链式调用接口有多个,且依赖上一个接口的输出结果,所以如果优化为异步的,需要业务人员的介入了,暂时先给客户记着。
5. 大型第三方库的执行时间
很多 toB
的应用都要选用一些大型的第三方库,本次这个项目也不例外,因为需要做 excel
相关的功能,所以客户采购了 SpreadJS
,是一个做 Web Excel
很出名的库,能把 js
库做成付费产品,也能看出还是有一番实力的。
功能的强大也就意味着初始化的时间会更长,用性能观测发现大概需要 500ms
左右,但客户的需求是2s
总共也没几个 500ms
,更换库也是不现实的,大量业务代码是基于这个库的。
目前我对脚本的加载做了 defer
,就是不想让 excel
的渲染阻塞其他元素的渲染,让页面除 excel
以外的区域能够更快的展示。对整体加载时间影响不大。
但是不知道是不是调整加载顺序的影响,使用 defer
+ $(document).ready(function(){})
后,打开 excel
速度提升了 200ms
。
对于这个库还有个优化思路,就是要将业务中用 excel
的功能模块划分清楚,轻量的话,只加载一个主js,按需去加载相应的插件,这样会让加载时间和执行时间都缩短一些。这个也需要业务人员耗费精力去梳理才能做到。
6. GZIP & HTTP2.0
在 lighthouse
上指标还是比较明显的,实际观感不大,可能是因为有浏览器缓存,所以加载时间对整体的影响不大,对首次打开的用户会有一些帮助。
7. 结局
最终页面打开速度来到了 3s
左右,未达到客户预期,白屏优化客户还是比较满意的。看在我辛苦的份上,客户也是发来了让我期盼已久的回应。
优化是在自己的空闲时间做的,大概总时长是 12 个小时左右(4天,每天3小时左右),收获 1000 元,刨除平台扣除,剩余 800 。
除了金钱的收益外,还有两点潜在的收益,也还是蛮不错的
- 认识了一位客户,且给客户留下印象不错
- 加深了自己对性能优化的理解
这就是整个故事的经过啦,对更详细的过程有兴趣的朋友,可以私信一下,我知道的都会尽量告诉你们哒。如果文章对你有所帮助,帮忙点个赞,感谢路过的朋友的支持。