前端性能优化 您所在的位置:网站首页 edge预加载 前端性能优化

前端性能优化

2024-03-01 15:55| 来源: 网络整理| 查看: 265

最近我负责的一项B端项目无法使用SSR(可参考我之前写的文章:我为什么没用上NextJS)。为了提升首屏加载体验,我选择了另一种优化方法——数据 + 图片预加载。

项目形态

带图片的项目页面

我负责的项目的页面很类似于上图所展示的页面。这个页面的特点是,内容区的图片都属于必要内容,因此在所有图片加载完成后才算作是首屏加载完毕。另一个特点是页面的图片尺寸相对较大,因此图片下载时间较长。然而,由于成本的考量,图片服务提供方并不愿意对图片进行压缩。

图片下载时间超过1秒了

可以看到,图片的大小已经到了 1.2MB,下载时间大约1秒。

根据实验室数据显示,该页面的首次内容绘制时间(LCP)大概需要2.4秒。考虑到在正式上线后,页面的首屏时间预计会超过这个水平,因此优化的空间仍然存在。

链路分析

优化前的链路

上图展示的是使用Chrome Performance 工具分析的整个页面加载过程。我之前也用过 Performance 工具分析过剪映的性能,有兴趣的同学可以看下。

该页面可以粗略地分为4个阶段:

HTML加载:页面的HTML内容加载也需要时间,耗时为200毫秒。 JS加载:包括前置JS资源、主JS和页面JS的加载。这是典型的自行实现路由的单页应用页面JS加载链路,耗时为900毫秒。 后台接口请求:耗时为300毫秒。 列表图片:首屏加载完成需要等待列表图片加载完毕,耗时为1000毫秒。

该性能链路的问题在于:图片的下载需要在JS加载和后台接口请求之后才会执行。事实上,我们可以将这个串行的链路改为并行的。

优化后的链路

优化后的链路

如上图所示,后台接口在所有JS加载之前发起请求,获取到图片的URL后,预加载这些图片。当渲染进程需要使用这些图片时,图片数据已经准备就绪,可以直接进行渲染。

为什么这种修改可以减少首屏时间呢?这是因为浏览器拥有两个线程:主要JS线程和网络线程,这两个线程能够并行工作。因此,在主要JS线程运行时,可以尽可能多地利用网络线程来请求资源,以最大限度地提高浏览器的性能。

尽管后台接口的发起逻辑被放置在所有JS加载之前,但由于JavaScript文件的执行优先级高于XHR请求,因此会有200毫秒的延迟才会触发后台请求。

进行链路优化后,实验室中的首屏时间从原来的2.4秒优化到1.7秒,节约了0.8秒的首屏时间。

代码实现

实现图片预加载的主要要点是将后台请求和图片请求放到其他JS加载之前。例如有这样一个 index.html:

Document

然后是实现预加载请求逻辑, prefetch-data.js:

window.prefetchData = {}; window.onPrefetchData = {}; // 模拟预加载一个用户后台接口 const prefetchUserData = async () => { const apiKey = "userData"; const res = await fetch("/api/getUserList"); // 预加载图片 res.images.forEach((imageUrl) => { new Image(imageUrl).src = imageUrl; }); // 放入缓存中 window.prefetchData[apiKey] = res; // 如果使用方的逻辑执行比 api 早,则调用使用方的数据回调 window.onPrefetchData[apiKey]?.(res); }; prefetchUserData();

在实际项目中,我们可以通过构建工具将上述代码合并成一个chunk后插入到html中。本项目使用的是Webpack,在配合Webpack的entry配置和html-webpack-plugin,实现了这个效果。

通过浏览器的 new Image() 的方式,可以预加载图片到缓存中,等有需要的时候再使用。

在主JS获取数据的方式如下所示,index.js:

const getUserData = () => { return new Promise((resolve) => { const apiKey = "userData"; // 如果已经请求完毕,则直接使用 if (window.onPrefetchData[apiKey]) { resolve(window.data); return; } // 如果还没请求完毕,则在window上挂载一个监听函数 window.onPrefetchData[apiKey] = (data) => { resolve(data); }; }); }; const init = async () => { const data = getUserData(); console.log(data); }; init();

index.js 需要跨越 JS 获取 prefetch-data.js 的内容,可以通过将变量挂载到window的方式实现数据的沟通。

总结

做性能分析时,我强烈推荐使用Chrome Performance工具来绘制页面的整体链路图,并在配合每个链路项的耗时分析的基础上,识别页面性能瓶颈所在。这次优化也是通过链路分析的方式才能证明数据 + 图片预加载的方式是可行的。然而,性能优化并非一劳永逸,每个页面都有不同的性能优化方案。因此,我在这里仅提供了一种思路,最终的优化策略还需开发人员根据具体情况进行深入思考。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有