http 请求
如果你学过计算机网络你就会知道, 我们请求一个带有 n 张图片的 html 文件实际上会发送 n+1 次请求, 因为在浏览器解析 html 的时候遇到了 src, 就会请求 src 后面的内容.
设想一下如果我们的页面有 1000000 张图片, 那么如果等待这些图片响应成功并加载完时延是非常大的, 而且图片的加载是同步的, 加载时会阻塞浏览器继续向下解析, 用户体验非常差.
那么我们可不可以让图片按需加载呢? 当图片出现在可视区的时候再加载它而不是一开始就加载完全部图片.
图片懒加载
- template:
- <div @scroll="lazyLoad" ref="lazy">
- <img v-for="(src, index) in imgs" src="##" :dataSrc="src" :key="index">
- <!--more img-->
- </div>
改变图片 src
监听最外层 div 的滚动事件, 触发滚动时遍历图片检测图片位置, 若在可视区内则显示
- loadImg() {
- var img = this.$refs.lazy.getElementsByClassName("lazyImg");
- // 已滚动高度 + 可视区高度
- var top = this.$refs.lazy.scrollTop + this.$refs.lazy.clientHeight;
- for(var i = 0; i <img.length; i++) {
- if(img[i].offsetTop <= top) { // 在可视区内则显示图片
- img[i].src = img[i].getAttribute("datasrc");
- }
- }
- },
- lazyLoad() {
- this.loadImg();
- }
以上就实现了一个图片懒加载, 本篇文章就到这里, 再见.
桥豆麻袋, 突然发现一个严重的问题: 滚动过程中会不断触发 lazyLoad 对图片做一个遍历并判断, 那么就会做无数次 for 循环, 更可怕的是, 修改一次 src 会发送一个请求, 在滚动的时候我们的 for 循环每次都从头判断并修改 src 请求图片, 那么请求次数可想而知.
函数防抖
如果在滚动过程中不断触发遍历并判断图片是否在可视区的监听事件, 会耗费很大的性能, 这里采用函数防抖: 当用户停止滚动时统一遍历判断图片位置
- debounce(fn) {
- // 函数防抖: 用户停止操作之后触发
- clearTimeout(this.timer);
- this.timer = setTimeout(() => {
- fn();
- }, 1000);
- }
我们可以将加载图片的方法放在 debounce 中
- lazyLoad() {
- this.debounce(this.loadImg);
- }
这样当用户滚动页面时, 松开手才会执行 loadImg 来遍历判断图片位置.
又出现了一个问题: 如果用户在滚动时从页面底部上拉到顶部一直没有松手, 那么在这期间都不会执行 loadImg, 这意味着页面的图片都不会显示, 非常影响用户体验
防抖优化
我们规定, 若用户上拉高度大于 500px 那么就自动加载一次可视区内图片, 这里我们用 oldScrollTop 记录上次上拉高度
- lazyLoad() {
- // 如果上拉距离大于 500px 则自动加载
- if(this.$refs.lazy.scrollTop - this.oldScrollTop> 500) {
- this.loadImg();
- this.oldScrollTop = this.$refs.lazy.scrollTop; // 更新 oldScrollTop
- } else { // 如果向下拉但小于 500px 则防抖加载
- this.debounce(this.loadImg);
- }
- }
下拉优化
当用户下拉的时候我们并不需要执行 lazyLoad, 因为我们之前的图片已经加载过了, 所以可以修改一下 lazyLoad
- lazyLoad() {
- // 如果上拉距离大于 500px 则自动加载
- if(this.$refs.lazy.scrollTop - this.oldScrollTop> 500) {
- this.loadImg();
- this.oldScrollTop = this.$refs.lazy.scrollTop;
- } else if(this.$refs.lazy.scrollTop - this.oldScrollTop < 0) { // 如果向下拉则不做操作
- return ;
- } else { // 如果向下拉但小于 500px 则防抖加载
- this.debounce(this.loadImg);
- }
- }
减少遍历个数
最重要的优化已经做完了, 但是还可以从一些小细节更加优化一下, 我们的 loadImg 方法中每次都是从 0 号下标开始遍历检查图片, 但是在用户上拉操作之后一部分图片已经被加载了, 就不需要再次去检查了.
我们可以用一个变量 len 记录上一次被加载后的最后一个图片, 然后修改一下 loadImg
- loadImg() {
- var img = this.getImages();
- var top = this.$refs.lazy.scrollTop + window.screen.height;
- // 从 len 开始检查
- for(var i = this.len; i < img.length; i++) {
- if(img[i].offsetTop <= top) {
- img[i].src = img[i].getAttribute("datasrc");
- this.len = i; // 更新 len
- }
- }
- }
结语
一个完整的优化版图片懒加载就完成了, 我将该功能封装成了一个 vue 的插件
源码: https://github.com/windlany/vue-plugins
该插件库会持续更新, 欢迎 star & fork~
来源: https://www.cnblogs.com/wind-lanyan/p/9173755.html