多图片优化方式总结
图片懒加载
图片懒加载,长列表最大的问题就是图片太多,如果一次性把图片全部请求了,那么页面渲染速度会很慢,如果用户点不到,还会造成很大的浪费,甚至会有性能瓶颈。
为什么要使用懒加载呢?为了加速页面的加载速度,减少不必要的请求,可以将未出现在可视区域的图片暂不加载,等到滚动到可视区域后再去加载。这样提升了性能和提高了用户体验。
实现原理:初始状态,所有图片都有一个默认的 src, 指向本地的一个 默认图片 default.png , 并且把img的真实的地址放在 data-src上。当滚动时,判断元素是否在可视区域,如果在可视区域,那么再把 data-src 上的值写入真正的 src 中。
实现方法
判断元素是否在可视区域的方法
方法一
通过 document.documentElement.clientHeight 获取屏幕可视窗口高度
通过 element.offsetTop 获取元素相对于文档顶部的距离
通过 document.documentElement.scrollTop 获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离
然后判断 offsetTop - scrollTop < clientHeight ,代表在可视区。
方法二
通过getBoundingClientRect()方法来获取元素的大小以及位置
The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.
这个方法返回一个名为ClientRect的DOMRect对象,包含了top、right、botton、left、width、height这些值。
假设const bound = el.getBoundingClientRect();来表示图片到可视区域顶部距离;
并设 const clientHeight = window.innerHeight;来表示可视区域的高度。
随着滚动条的向下滚动,bound.top会越来越小,也就是图片到可视区域顶部的距离越来越小,当bound.top===clientHeight时,图片的上沿应该是位于可视区域下沿的位置的临界点,再滚动一点点,图片就会进入可视区域。
也就是说,在bound.top<=clientHeight时,图片是在可视区域内的。
function isInSight(el) { const bound = el.getBoundingClientRect(); const clientHeight = window.innerHeight; //如果只考虑向下滚动加载 //const clientWidth = window.innerWeight; return bound.top <= clientHeight + 100; // +100是为了提前加载。 }
特别简单的一个实例
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <style type="text/css"> *{margin:0;padding: 0;} img {display: block;width: 100%;height: auto;} </style> </head> <body> <img src="img/default.png" data-src="img/img1.png"> <img src="img/default.png" data-src="img/img2.png"> <img src="img/default.png" data-src="img/img3.png"> <img src="img/default.png" data-src="img/img4.png"> <img src="img/default.png" data-src="img/img5.png"> <img src="img/default.png" data-src="img/img6.png"> <img src="img/default.png" data-src="img/img7.png"> <img src="img/default.png" data-src="img/img8.png"> <img src="img/default.png" data-src="img/img9.png"> <img src="img/default.png" data-src="img/img10.png"> <script type="text/javascript"> var imgs = document.querySelectorAll('img'),len = imgs.length; var n = 0;//存储图片加载到的位置,避免每次都从第一张图片开始遍历 window.onscroll = function() { var seeHeight = document.documentElement.clientHeight; var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; for (let i = n; i < len; i++) { if(imgs[i].offsetTop < seeHeight + scrollTop) { if (imgs[i].getAttribute('src') == 'img/default.png'){ imgs[i].src = imgs[i].getAttribute('data-src'); } n = i + 1; console.log('n = ' + n); } } }; </script> </body> </html>
函数节流
在类似于滚动条滚动等频繁的DOM操作时,总会提到“函数节流、函数去抖”。
所谓的函数节流,也就是让一个函数不要执行的太频繁,减少一些过快的调用来节流。
基本步骤:
- 获取第一次触发事件的时间戳
- 获取第二次触发事件的时间戳
- 时间差如果大于某个阈值就执行事件,然后重置第一个时间
function throttle(fn, mustRun = 500) { const timer = null; let previous = null; return function() { const now = new Date(); const context = this; const args = arguments; if (!previous){ previous = now; } const remaining = now - previous; if (mustRun && remaining >= mustRun) { fn.apply(context, args); previous = now; } } }
这里的mustRun就是调用函数的时间间隔,无论多么频繁的调用fn,只有remaining>=mustRun时fn才能被执行。
简单封装
const checkVisible = (ele) => { let scrollTop = document.documentElement.scrollTop || document.body.scrollTop let clientHeight = document.documentElement.clientHeight return ele.offsetTop - scrollTop < clientHeight } let imgs = document.getElementsByTagName("img"), nums = imgs.length const lazyLoad = () => { for (let i = 0; i < nums; i++) { if (checkVisible(imgs[i]) && imgs[i].getAttribute("src") === "default.png") { imgs[i].src = imgs[i].getAttribute("data-src") } } } function throttle(fn, mustRun = 500) { const timer = null; let previous = null; return function() { const now = new Date(); const context = this; const args = arguments; if (!previous){ previous = now; } const remaining = now - previous; if (mustRun && remaining >= mustRun) { fn.apply(context, args); previous = now; } } }
从图片本身优化
一般问题出在哪,就得追本溯源。目前我们能够用到的有以下几种方法。
- 更换图片格式;
- 通过特别的方式在保持清晰度的条件下来压缩图片,目前常用的是在https://tinypng.com/该网站压缩图片,一般压缩率很高;
- 由 png,jpg,jpeg,gif 转换为更好的 webp。具体webp详看(https://www.zhihu.com/question/27201061)
WebP 的优势在与它更好的图像数据压缩算法,能够将图片转换为更小的体积,具有无损和有损的压缩模式。如果是选择了有损压缩,也拥有肉眼无法识别差异的图像质量。虽然它在页面渲染的时候浏览器比jpg会花稍长的时间解析它的算法,但是权衡它所带来的体积的减少来看,WebP 还是最优秀的。目前Google、Facebook、腾讯、阿里、美团的等国内外互联网公司广泛应用了webp,超过70%的浏览器已经支持webp,Safari和Foxmail也在进行支持webp的测试。
面临困难:
- 目前并不是所有浏览器都支持WebP,因此需要解决浏览器适配问题。
- 对于已上线运营的网站,采用WebP需要替换大量图片,工作量太大。
针对 WebP 兼容性问题,可以从两个方面入手
1.从服务端考虑:
如果浏览器支持 WebP ,那么会在 request header accept里,发送image/webp, 服务器收到以后根据这个来去返回给客户端图片。请求头里有,则发送webp的,如果没有,就发送普通格式的图片。
不过这个对服务器要求比较高,并且现在图片大多放在 CDN 上,CDN去做这种策略可能会稍微麻烦点。
2.从前端考虑:
前端去检测浏览器是否支持 WebP, 支持就发送 WebP 的图片请求,不支持就发送 jpg、png等。下面是一行代码判断是否支持WebP。
var isSupportWebp = !!\[\].map && document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0;
!!\[\].map主要是判断是否是IE9+,以免toDataURL方法会挂掉。如果你直接对数组原型扩展了map方法,则需要使用!!\[\].map以外的方法进行判断,例如!!window.addEventListener等。

浙公网安备 33010602011771号