Appearance
万物皆可懒加载
前言
最好的代码,就是没有代码。
同样的,体验最好的网页,就是没有内容的网页。
这段话乍一听似乎脱离实际,没有内容的网页根本没有加载的必要。
但静下来分析,却道出了前端体验优化的核心:精简页面内容。
再多优化,都比不上从根源上精简页面的内容,减少加载的资源。
所以,此节内容为大家讲述:
前端工程精简加载资源体积和资源数量的解决方案:懒加载各类资源。
资源懒加载一般应用于需要加载外部资源的元素,例如 <img>
, <video>
, <iframe>
, <picture>
等。
当懒加载目标元素在视口(ViewPort)外时,不加载对应资源。目标元素接近或进入视口时,才触发加载资源。
从而减少页面加载资源的数量,精简加载资源体积,优化用户体验。
作者个人简介:Ned同学的个人说明书
大纲(学完则删):
三类资源懒加载实现方案
1、监听滚动事件方案
第一种实现方案是通过监听浏览器页面滚动事件(scroll),动态计算视口内元素位置,从而判断懒加载目标元素,触发资源加载。
以图片元素懒加载为例,我们需要监听页面滚动事件,并计算出每张图片与视口顶部的相对距离。
如果图片在视口内,就加载该图片,否则就不加载。
html
<img data-lazyload data-src="/isNed.jpg" />
js
// 1. 获取所有需要懒加载的图片元素
const lazyloadImages = document.querySelectorAll('[data-lazyload]');
// 2. 监听页面滚动事件
window.addEventListener('scroll', () => {
lazyloadImages.forEach(img => {
if (img.getAttribute('data-loaded')) {
return;
}
// 3. 获取图片与视口顶部的相对距离
const topPos = img.getBoundingClientRect().top;
// 4. 与 视口高度(window.innerHeight)对比,判断是否在视口内
if (topPos < window.innerHeight) {
// 5. 如果图片在当前视口内,就加载该图片
img.src = img.getAttribute('data-src');
img.setAttribute('data-loaded', true)
}
});
});
在上述代码中,我们首先获取所有需要懒加载的图片元素,通过 querySelectorAll 获取带有指定的懒加载标记属性 data-lazyload 的元素引用,这些图片初始化时没有设置src值,而真正的懒加载目标图片URL则保存在 data-src 属性中,这样就能实现初始化时暂不加载图片,由代码逻辑控制图片何时加载。
然后,我们监听页面滚动事件 window.addEventListener('scroll'),在滚动事件的回调中,遍历所有需要懒加载的图片元素 lazyloadImages.forEach。
对于每个图片元素,我们首先检查其 data-loaded 属性是否为 true,如果是,则说明该图片已经加载过了,我们就跳过该元素。
否则,我们通过 getBoundingClientRect() 方法获取该图片距离视口(viewport)顶部的位置,并通过和当前视口高度(window.innerHeight)对比,判断该图片当前是否在视口内,即图片与视口顶部的距离,是否小于当前视口的高度。
最后,如果图片在视口内,就将该图片的 src 属性设置为 data-src 属性的值,从而触发图片资源加载,并将 data-loaded 属性设置为 true,表示该图片已经加载过了。
这样我们就实现了对图片类资源的懒加载,初始化时暂不加载图片资源,减少加载资源数量和体积,提高初始化时渲染性能,当图片进入浏览器视口时,再真正触发加载。
实际应用时,我们应该对这一方案做更多细节上的优化,如:
- 设置图片默认的宽高,以避免懒加载完成后,页面高度变化,被判断为意外布局变化,影响 CLS 评分。
- 添加图片未加载占位符和加载中动画,改善用户等待加载时的视觉和体验。
- 计算位置、判断目标元素是否出现在视口时,还要考虑水平方向上的页面滚动位置,计算 getBoundingClientRect() 返回的 top, bottom, left, right 4个方向相对视口位置。
- 对 scroll 事件添加节流优化,降低触发回调函数的频率,避免影响页面渲染的FPS。
- 依赖 scroll 事件,容易对页面渲染性能产生负面影响,这也是监听滚动事件方案的主要痛点。
过低的 scroll 事件回调触发频率,会导致懒加载触发不灵敏。
而过高的 scroll 事件回调触发频率,又会因为大量计算,导致JS执行耗时太长,阻塞UI绘制,产生页面卡顿。