H5 页面秒开优化与实践
背景
3 月份针对线上重点 H5 项目秒开进行治理,本文将逐步介绍如何通过 H5 页面的优化手段来提高 1.5 秒开率。
为什么要做优化?
从用户角度看,优化能够让页面加载得更快、对用户操作响应更及时,用户体验更良好,提升用户体验和降低用户流失率非常重要。其中 Global Web Performance Matters for ecommerce报告中也有具体说明优化的重要性。
从企业角度看,优化能够减少页面请求数或者减小请求所占带宽,能够节省可观的资源成本,最终提高收益转化。
优化目标
从上图中可以看出,有些域名下可能低于 90%,最高的也没达到 96%,离既定 98%的目标还有一定差距。
H5 性能分析
分析工具
Lighthouse
Chrome DevTools
Webview 加载 H5
通常情况分以下几个阶段
Webview 初始化。
到达新的页面,网络连接,从服务器下载 html,css,js,页面白屏。
页面基本框架出现,js 请求页面数据,页面处于 loading 状态。
出现所需的数据,完成整个页面的渲染,用户可交互。从图形直观看 H5 启动过程:
如何缩短这些过程的时间,就成了优化 H5 性能的关键。接下来我们详细看一下各个阶段注意的优化点。
优化方案
从以下几个方面入手:
加载策略优化
增加骨架屏
资源请求优化(静态资源、图片以及 webp 、图片懒加载、组件按需加载)
打包资源优化
CDN & 缓存
接下来就逐个分析
加载策略优化
先看一张图:
从这张图里我们能看到什么,大致能总结为以下四点:
默认情况:HTML 解析,然后加载 JS,此时 HTML 解析中断,然后执行 JS,最后 JS 执行完成并恢复 HTML 解析。
defer 情况下:HTML 和 JS 并驾齐驱,最后才执行 JS( js 脚本在所有元素加载完成后执行,而且是按照 js 脚本声明的顺序执行,但要等到 dom 文档全部解析完才会被执行)。
async 情况下:HTML 和 JS 并驾齐驱,JS 的执行可能在 HTML 解析之前就已完成了 (js 脚本是乱序执行的,不管你声明的顺序如何,只要某个 js 脚本加载完就立即执行)。
module 情况下:与 defer 情况类似,只不过在提取的过程中会加载多个 JS 文件而已 (声明 acript 标签 type="module"属性从而拥抱 es6 的模块导入导出语法, 加载也和 defer 差不多,只不过可以加载多个 JS 文件而已)。
项目中实践示例:
预加载
prefetch 和 preload
preload 是一个新的 Web 标准,在页面生命周期中提前加载你指定的资源,同时确保在浏览器的主要渲染机制启动之前。
具体使用如下:
注意:preload 紧挨着 title 放,使其最早介入。
prefetch 是提示浏览器,用户在下次导航时可能会使用的资源(HTML,JS,CSS 或者图片等),因此浏览器为了提升性能可以提前加载、缓存资源。prefetch 的加载优先级相对较低,浏览器在空闲的时候才会在后台加载。用法与 preload 类似,将 rel 的值替换成 prefetch 即可。
preload 是告诉浏览器页面必定需要的资源,浏览器一定会加载这些资源,而 prefetch 是告诉浏览器页面可能需要的资源,浏览器不一定会加载这些资源。所以建议:对于当前页面很有必要的资源使用 preload,对于可能在将来的页面中使用的资源使用 prefetch。
注意:用 preload 和 prefetch 情况下,如果资源不能被缓存,那么都有可能浪费一部分带宽,请慎用。非首页的资源建议不用 preload,prefetch 作为加载下一屏数据来用。
dns-prefetch 和 preconnect
dns-prefetch
DNS 请求需要的带宽非常小,但延迟较高,这点特别是在手机网络上比较明显。预读取 DNS 能让延迟明显减少一些(尤其是移动网络下)。为了帮助浏览器对某些域名进行预解析,你可以在页面的 html 标签中添加 dns-prefetch 告诉浏览器对指定域名预解析。
dns-prefetch 是一项使浏览器主动去执行域名解析的功能。dns-prefetch 应该尽量的放在网页的前面,推荐放在后面。具体使用方法如下:
洗车项目中有体现:
注意:dns-prefetch 需慎用,推荐首屏加载资源添加 DNS Prefetch
preconnect
和 DNS prefetch 类似,preconnect 不仅会解析 DNS,还会建立 TCP 握手连接和 TLS 协议(如果是 https 的话)。用法如下:
preconnect
允许浏览器在 HTTP 请求实际发送到服务器之前建立早期连接。可以预先启动 DNS 查找、TCP 握手和 TLS 协商等连接,从而消除这些连接的往返延迟并为用户节省时间。
骨架屏
从图上可以看出有白屏情况,FCP 时间超过了 1 秒多,解决下来就用了骨架屏来解决白屏情况 并提升 FCP。
骨架屏
就是在页面资源尚未加载完成以及渲染尚未完成时,需要先给用户的展示页面大致结构。直到资源加载完成以及渲染完成后,使用渲染的页面。骨架屏处理方案也很多,常用方案有以下几种:
首屏:可以在
index.html
模版中手写骨架屏相关代码。其他页面:可以利用 UI 提供 SVG 图
作为 SPA 中路由切换的 loading:需自己编写骨架屏,推荐两个成熟方便定制的 svg 组件去定制骨架屏- react-content-loader和vue-content-loader。
骨架图渲染前不要出现任何网络请求,在此之前 HTML 内容不要超过 4KB。
我这里采用了固定的骨架屏 SVG 打包自动注入到模板方式。并产出了基于 vite 的自动化注入骨架屏和无阻塞缓存资源文件@auto/vite-plugin-cdn私有插件。
举个🌰:
资源请求优化
图片压缩和 webp
图片是网站性能优化需要重点关注的方向。为什么这么说呢?来看个图片:
一般 UI 提供的切图都是未通过压缩的图片,所有在开发过程中,我们必须再压缩一次。如果压缩后的图片还是大于 500KB 就要考虑将图片分割成多张。目前市面上图片压缩比较多,给大家推荐个好用的工具(docsmall)。可批量压缩各类图片。
WebP 的优势体现在它具有更优的图像数据压缩算法,在肉眼识别无差异的图像质量情况下带来更小的图片体积的优势;同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性,在 JPEG 和 PNG 上的转化效果都相当优秀、稳定和统一。内部提供了图片资源可以上传到 前端加速服务 或 前端静态资源服务内部资源库会自动生成 webp 格式,可以在项目打包的时候处理图片时加上 format=webp 即可,接口动态图片可采用 @auto/img-crop私有包做裁切同时也可通过参数动态支持 webp 和设置缓存时间。
webp 前后对比:
从对比结果看,同图片采用 webp 大小至少减少了 50%,越大的图优化比例越大。大幅减少了文件体积,缩短了加载的时间,大页面图片量较多的场景下,页面的渲染速度是有较大提升的。
CDN & 缓存
上面提到了前端加速服务 或 前端静态资源服务内部服务均集成 CDN 功能。具体情况可以参考使用文档。
结合以上两个服务的应用能很好的处理资源问题,目前我们的新 SPA 项目都发布到了前端加速服务上。如图:
资源文件自动都有缓存
未覆盖的 CDN
从图上看左图没命中缓存,右图则命中缓存,很多项目由于域名接口和网页接口一样 CDN 就是没开启缓存,我们后通过域名 Path 来针对开启 CDN 缓存。
打包资源优化
提取第三方库
通常情况下,大多第三方库的代码不做版本升级是不会发生变化的 ,这时就可以用到 DllPlugin:把复用性较高的第三方库打包在一起,不升级就不需要重新打包。
这样做的优点:
提取的第三方库生成的资源版本号(资源的访问连接)不会变,提高了缓存的利用;
避免打包出单个文件的大小太大,不利于加载;
每次构建只重新打包业务代码,提高打包效率。
为了让前端页面性能更优, App WebView 中针对 React
、Vue
、Zepto
三大常用框架相关资源及 Polyfill
进行了预加载处理,所以我们把这些固定的资源调整为无阻塞的预加载地址。具体如何使用 App H5提供了 webpack 的相关配置说明。
这里针对 vite 的配置做些说明:
这里@auto/vite-plugin-cdn私有插件中提供正常骨架屏、预加载资源、处理资源加载顺序
示例:
优化打包资源
我们来看一组图:
从图上看优化前后,文件数从 295 个减少到 214 个, 大小从 1.63MB 减少到 439.88KB,大小降了 73.6460%
webpack 和 vite 配置
设置预警来检验打包文件
资源(asset)是从 webpack 生成的任何文件。此选项根据单个资源体积(单位: bytes),控制 webpack 何时生成性能提示。用法:
将打包后的静态资源控制在 300KB 以内,最终通过 Gzip 压缩后,基本都在 100KB 以内。其他的优化包括提取第三方库、移除调试和无用代码、Tree Shaking 等。
总结
经过以上的一系列的优化实施,我们来看一下优化前后数据的对比:
如果你读完了也不妨点个👍哟,万分感谢!
参考文献
https://www.keycdn.com/support/preconnect
评论