在前端摸爬滚打了8年,以前做的主要是B端项目,所以很少能接触到性能优化方面的需求。
最近我们面向C端用户的产品首页图片比较多,产品在给老板演示时,发现图片加载速度很慢。
之前虽然设置了图片缓存,但架不住用户首次打开;而且之前的分页在最近一次调整中临时去掉了,导致首页需要加载50张高清大图,产品也没压缩,不卡才怪。当然也有我的锅,去掉分页后没有做懒加载。
所以,我决定对首页图片进行懒加载优化,不就是计算滚动top设置图片src吗?我都懒得写。
然后,我果断给TRAE下达了任务。

看着TRAE吭哧吭哧的干活,我悠闲的喝了一口咖啡。喝完之后,TRAE也差不多干完活了,我刷新了浏览器后滚动鼠标到底部,图片才开始加载。完美!
我开始看TRAE的代码,封装的真好啊,不过咋有点看不懂?IntersectionObserver是啥,Observer倒有点眼熟,连在一起是真不知道。
于是我点开了MDN的文档,被迫学习了一下!发现IntersectionObserver是一个非常方便的API,它可以监听元素是否进入视口。都不需要前端自己计算滚动top了,浏览器自己就可以监听。
果然科技是在进步的,就如我们现在指挥AI干活。
TRAE封装的代码如下(lazyload.ts):
typescript
/** * 图片懒加载工具函数 * 实现首次只渲染可视区域图片,滚动后加载其它图片 */interface LazyLoadOptions { threshold?: number rootMargin?: string}/** * 创建IntersectionObserver观察器 * @param callback 回调函数 * @param options 配置选项 * @returns IntersectionObserver实例 */const createObserver = ( callback: (entries: IntersectionObserverEntry[]) => void, options: LazyLoadOptions = {}): IntersectionObserver => { const defaultOptions: IntersectionObserverInit = { threshold: options.threshold ?? 0.1, rootMargin: options.rootMargin ?? '0px', } return new IntersectionObserver(callback, defaultOptions)}/** * 加载图片 * @param img 图片元素 */const loadImage = (img: HTMLImageElement): void => { if (!img || !img.dataset.src) { return } img.src = img.dataset.src img.removeAttribute('data-src')}/** * 初始化图片懒加载 * @param selector 图片选择器 * @param options 配置选项 * @returns IntersectionObserver实例 */export const initLazyLoad = ( selector: string = 'img[lazy]', options: LazyLoadOptions = {}): IntersectionObserver => { const images = document.querySelectorAll<HTMLImageElement>(selector) if (images.length === 0) { console.warn('未找到需要懒加载的图片元素') return createObserver(() => {}) } const observer = createObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const img = entry.target as HTMLImageElement loadImage(img) observer.unobserve(img) } }) }, options) images.forEach((img) => { if (img.dataset.src) { observer.observe(img) } }) return observer}/** * 手动加载单张图片 * @param img 图片元素或图片元素ID */export const loadSingleImage = (img: HTMLImageElement | string): void => { const imageElement = typeof img === 'string' ? document.querySelector<HTMLImageElement>(img) : img if (imageElement) { loadImage(imageElement) }}/** * 销毁懒加载观察器 * @param observer IntersectionObserver实例 */export const destroyLazyLoad = (observer: IntersectionObserver): void => { if (observer) { observer.disconnect() }}