Fork me on GitHub

web性能优化-首屏和白屏问题

白屏时间是指浏览器从响应用户输入网址地址,到浏览器开始显示内容的时间。

首屏时间是指浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间。

白屏时间 = 地址栏输入网址后回车 - 浏览器出现第一个元素

首屏时间 = 地址栏输入网址后回车 - 浏览器第一屏渲染完成

影响白屏时间的因素:网络,服务端性能,前端页面结构设计

影响首屏时间的因素:白屏时间,资源下载执行时间

为什么打开一个H5页面会有一长段时间白屏?因为它做了很多事情,大概是:

初始化webview->请求页面->下载数据->解析HTML->请求js/css资源->dom渲染->解析JS执行->JS请求数
据->解析渲染->下载渲染图片

一些简单的页面可能没有JS请求数据这一步,但大部分功能模块应该是有的,根据当前用户信息,JS向后台请求相关数据再渲染,是常规开发方式。

一般页面在dom渲染后能显示雏形,在这之前用户看到的都是白屏,等到下载渲染图片后整个页面才完整显示,首屏秒开优化就是要减少这个过程的耗时。

前端优化

上述打开一个页面的过程有很多优化点,包括前端和客户端,常见的前端和后端的性能优化在桌面时代已经有最佳实践,主要的是:

  • 降低请求量:合并资源,减少http请求数,minify/zip压缩,懒加载,webP
  • 加快请求速度:预解析DNS,减少域名数,并行加载,CDN分发
  • 缓存:HTTP协议缓存请求,离线缓存manifest,离线数据缓存localStorage
  • 渲染:JS/CSS优化,加载顺序,服务端渲染

其中对首屏启动速度影响最大的就是网络请求,所以优化的重点就是缓存,这里着重说一下前端对请求的缓存策略。这里细分一下,分成HTML的缓存,JS/CSS/image资源的缓存,以及json数据的缓存。

HTML和JS/CSS/image资源都属于静态文件,HTTP本身提供了缓存协议,浏览器实现了这些协议,可以做到静态文件的缓存,总的来说就是两种缓存:

  • 询问是否有更新:根据if-Modified-Since/ETag等协议向后端请求询问是否有更新,没有更新返回304,浏览器使用本地缓存
  • 直接使用本地缓存:协议里的Cache-Control/Expires字段去确定多长时间内可以不去发送请求询问更新,直接使用本地缓存

前端能做的最大限度的缓存策略是:HTML文件每次都向服务器询问是否有更新,JS/CSS/image资源文件则不请求更新,直接使用本地缓存。那JS/CSS资源文件如何更新?常见的做法是在构建页面的过程中给每个资源文件一个版本号或hash值,若资源文件有更新,版本号和hash值变化,这个资源请求的URL就变化了,同时对应的 HTML 页面更新,变成请求新的资源URL,资源也就更新了。

json 数据的缓存可以用 localStorage 缓存请求下来的数据,可以在首次显示时先用本地数据,再请求更新,这都由前端 JS 控制。

这些缓存策略可以实现 JS/CSS 等资源文件以及用户数据的缓存的全缓存,可以做到每次都直接使用本地缓存数据,不用等待网络请求。但 HTML 文件的缓存做不到,对于 HTML 文件,如果把 Expires / max-age 时间设长了,长时间只使用本地缓存,那更新就不及时,如果设短了,每次打开页面都要发网络请求询问是否有更新,再确定是否使用本地资源,一般前端在这里的策略是每次都请求,这在弱网情况下用户感受到的白屏时间仍然会很长。所以 HTML 文件的“缓存”和跟“更新”间存在矛盾。