(3)Intersection Observer实现
Intersection Observer是HTML5新增的API,可以用来实现图片懒加载。MDN中对Intersection Observer的解释:
IntersectionObserver接口 (从属于Intersection Observer API) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport)被称为根(root)。
当一个
IntersectionObserver
对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦IntersectionObserver被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素。
上面使用节流来解决了scroll频繁触发的问题,这也说明传统的图片懒加载方案是存在一定问题的;而Intersection Observer不需要监听scroll事件,可以做到只要图片元素出现在可视区域内,就能进行回调,具体如下:
document.addEventListener("DOMContentLoaded", function() { var lazyImages = [].slice.call(document.querySelectorAll("img.lazy")); if ("IntersectionObserver" in window) { let lazyImageObserver = new IntersectionObserver(function(entries, observer) { entries.forEach(function(entry) { if (entry.isIntersecting) { let lazyImage = entry.target; lazyImage.src = lazyImage.dataset.src; lazyImage.srcset = lazyImage.dataset.srcset; lazyImage.classList.remove("lazy"); lazyImageObserver.unobserve(lazyImage); } }); }); lazyImages.forEach(function(lazyImage) { lazyImageObserver.observe(lazyImage); }); } }); 复制代码
上面就是使用Intersection Observer完成图片懒加载的方法,该方法唯一的缺点就是兼容性还不是很好,如果需要兼容版本较低的浏览器,则要根据浏览器的版本封装更通用的方法。
(4)第三方库实现
除了上面介绍的一些延迟加载方法之外,还可以借助一些已经封装好的第三方库,下面是一些成熟的第三方库:
- lozad.js 是超轻量级且只使用 Intersection Observer 的库, 因此它的性能极佳,但如果要在旧版本浏览器上使用,则需要配置polyfill。
- lazysizes 是功能全面的延迟加载库,其使用的模式与本文所示的代码示例非常相似,会自动与
<img>
元素上的lazyload
类绑定,然后在data-src
和/或data-srcset
属性中指定图像网址,该库还可以通过许多插件进行扩展,执行延迟各种资源等操作。 - 如果使用React框架,可以使用 react-lazyload来进行图片懒加载操作,这个库是React图片懒加载的主流解决方案。
- 如果使用Vue框架,可以使用 vue-lazyload 来进行图片懒加载操作,这个库是Vue图片懒加载的主流解决方案。
3. 首屏加载优化
随着Vue、React等框架的盛行,SPA单页面应用越来越多,多数的SPA应用的结构都很类似。由于SPA页面打包之后的JavaScript文件很大,等这个巨大的JavaScript文件加载完之后,首屏才能渲染,这就导致出现了白屏的问题。在移动端,一些需要快速迭代的开发项目都是使用HTML5开发的,同样首屏加载白屏问题非常的严重。下面就来看一下针对首屏渲染白屏的解决方案。
(1)骨架屏
骨架屏就是指在未加载完时,先简单的用图形勾勒出页面的大概布局,给用户一个视觉上更好一点的体验,等页面加载完成之后,再将骨架屏替换掉即可,如下图所示:
实现骨架屏的方式有很多种,下面来看下常见的几种:
- 绘制静态骨架屏
最直接的实现方案就是直接绘制出一张骨架屏的图片,在资源加载完毕之后,页面内容直接替换这张图片即可。我们知道,base64格式的图片可以直接插入到HTML中,并且可以节约一次HTTP请求,所以,可以把骨架屏的静态图片直接转化为base64格式并插入到HTML文件中。只要监听onLoad
事件,当完成之后,将图片替换掉即可。
- 代码绘制骨架屏
通过代码绘制骨架屏会比绘制静态图片更加灵活,可定制化程度高。可以根据自己的需要,绘制需要的样式。只需要根据首屏的大致轮廓绘制一个对应HTML结果,再用CSS填充上合适的背景色即可。除此之外,还可以通过改动CSS,为这个骨架图添加一些动态的效果(加载动画),这样用户体验会更好。
- 自动化解决方案
如果需要给多个页面添加骨架图,那么就需要绘制多个骨架图,这样就比较麻烦。不过,现在已经有以下比较成熟的解决方案,目前使用比较广泛的是 page-skeleton-webpack-plugin插件,该插件是一个webpack插件,可以根据具体的页面生成对应的骨架屏,使用起来非常方便。除此之外,Ant Design也提供了骨架屏的方案:Skeleton骨架屏
(2)资源预加载
上面提到了预加载,MDN中对资源预加载的解释:
页面资源预加载(Link prefetch)是浏览器提供的一个技巧,目的是让浏览器在空闲时间下载或预读取一些文档资源,用户在将来将会访问这些资源。一个 Web 页面可以对浏览器设置一系列的预加载指示,当浏览器加载完当前页面后,它会在后台静悄悄的加载指定的文档,并把它们存储在缓存里。当用户访问到这些预加载的文档后,浏览器能快速的从缓存里提取给用户。
简单来说就是当页面加载完成后或者其他空闲的时间,可以加载之后页面中用到的资源。
1)rel="preload"
可以使用rel
属性,<link>
元素的 rel
属性的属性值preload
能够让我们在HTML页面中 <head>
元素内部书写一些声明式的资源获取请求,可以指明哪些资源是在页面加载完成后即刻需要的。
<link rel="preload" href="style.css" as="style"> <link rel="preload" href="main.js" as="script"> 复制代码
这样,我们预加载了css和js文件,在随后的页面渲染的时候,如果需要使用这两个文件,直接使用即可,因为已经缓存好了。不仅是这些文件可以预加载,字体,图片,音视频文件都可以进行预加载。
2)rel="subresource"
除此之外,我们还可以使用 subresource
属性。subresource 和 preload 属性相比,它的优先级是最高的,使用方法和 preload 基本相同,如下:
<link rel="subresource" href="styles.css"> 复制代码
因此,如果资源需要马上用到或者是当前页面急需的资源,就可以使用subresource。当然在实际的开发当中 preload 属性用的较多一些
3) rel="prerender"
prerender
可以指定加载一个页面的所有资源,使用方法如下:
<link rel="prerender" href="/result.html" /> 复制代码
prerender
的效果就和默认打开了一个隐藏的 tab 一样,会下载所有的资源、创建 DOM、渲染页面、执行 JS 等。如果用户进入指定的链接,隐藏的这个页面就会进入马上进入用户的视线。这个属性虽然强大,但是却不能乱用,一定要确定这个页面用户的确会进行点击,才可以进行设置。否则用户不点,就浪费了大量的宽带资源去下载了一个无用的页面,这样反而得不偿失。
4. 视频加载优化
(1)延迟加载视频
图片和视频这类静态资源资源占比都比较大。与图片一样,视频同样可以延迟加载,来达到优化性能的目的。正常情况下加载视频,使用的是<video>
标签,那么对于一些需要由用户自己播放的视频,最好指定<video>
标签的preload属性为none,这样浏览器就不会预加载任何视频数据。为了占用空间,可以使用poster属性为<video>
占位。实现如下:
<video controls preload="none" poster="replace.jpg"> <source src="main.webm" type="video/webm"> <source src="main.mp4" type="video/mp4"> </video> 复制代码
(2)视频代替GIF动画
在业务开发中,我们应尽量使用视频代替尺寸过大的GIF动画,虽然GIF动画应用范围很广, 但是其在输出文件大小、图像色彩质量等方面均不如视频。GIF动画相对于视频具有三个附加的特性:没有音轨、连续循环播放、加载完自动播放,替换成视频后类似于:
<video controls autoplay loop muted playsinline> <source src="main.webm" type="video/webm"> <source src="main.mp4" type="video/mp4"> </video> 复制代码
其中video标签中附加的属性的含义分别为:autoplay自动播放、muted静音播放、loop循环播放、playsinline用于在ios系统中自动播放。这样,视频就基本实现了一个GIF动画的效果。
实际上,很多浏览器并不能像Chrome浏览器一样,能自动进行延迟加载。下面就来通过一些配置,使得该场景的视频也能延迟加载。首先需要修改HTML标签:
<video controls autoplay loop muted playsinline height:"500" width: "800" poster="replace.jpg"> <source data-src="main.webm" type="video/webm"> <source data-src="main.mp4" type="video/mp4"> </video> 复制代码
这里为video标签添加了poster属性,用来指定图片为视频延迟加载出现前的占位,还使用了类似于图像懒加载的方式,将视频的真实地址放在了data-src中。下面就基于 Intersection Observer,用JavaScript实现对延迟加载的控制:
Document.addEventListener("DOMContentLoaded", () => { const lazyVideos = [].slice.call(document.querySelectorAll("video.lazy")); if("IntersectionObserver" in window) { const lazyVideoObserver = new IntersectionObserver((entries, observer) => { entries.forEach((video) => { if(video.isIntersecting) { for(const source in video.target.children) { const videoSrc = video.target.children[source]; if (typeof videoSrc.tagName === "string" && videoSrc.tagName === " source") { videoSrc.src = videoSrc.dataset.src; } } video.target.load(); video.target.classList.remove("lazy"); lazyVideoObserver.unobserve(video.target); } }) }) lazyVideos.forEach((lazyVideo) => { lazyVideoObserver.observe(lazyVideo); }); } }) 复制代码
对于视频的延迟加载有点类似于图片的懒加载,需要对所有source子元素进行迭代解析,将data-src的属性值迁移到src上。不同的是,需要额外显示调用元素的load方法来触发加载,然后视频才会根据autoplay属性开始进行自动播放,这样就能使用低于GIF动画的流量消耗,进行资源的延迟加载。