TODO: 懒加载

图片懒加载基础概念

height 和 width

  1. innerHeight/innerWidth 是返回窗口的文档显示区的高度和宽度(只包括网页的部分)

  2. outerHeight/outerWidth 是返回整个浏览器的高度和宽度(从浏览器最外部开始算)

  3. offsetWidth 获取物体宽度的数值, 这个实际的宽度是受盒模型的影响的。offsetWidth 实际获取的是盒模型(width+border + padding)

  4. image: naturalHeight, naturalWidth HTML5 的新属性,用来判断图片的真实宽度和高度。 但有个前提是,必须在图片完全下载到客户端浏览器才能判断,目前在 ie 9,Firefox, Chrome, Safari 和 Opera 都是可以使用的, 如果是不支持的版本浏览器,那可以用传统方法判断。

    var myImage = document.getElementById("myImage");
    if (typeof myImage.naturalWidth == "undefined") {
      // IE 6/7/8
      var i = new Image();
      i.src = myImage.src;
      var rw = i.width;
      var rh = i.height;
    } else {
      // HTML5 browsers
      var rw = myImage.naturalWidth;
      var rh = myImage.naturalHeight;
    }
    

特定属性

  1. data-srcset img 标签的预设 url, 自定义 html 属性可以一开始不加载 url 使用一个默认的很小的 base64 图片来显示,图片马上进入视图范围以内时,通过一个 loading 切换动画,并且监听器回调通过将预设的 url 替换到默认的 src 值上面,此时就会去发起 http 请求图片,达到一个懒加载的效果。

页面大量图片,如何优化加载,优化用户体验

  1. 懒加载:在页面的未可视区域添加一个滚动事件,判断图片位置与浏览器顶端的距离与页面的距离,如果前者小于后者,优先加载。
  2. 预加载:如果为幻灯片、相册等,可以使用图片预加载技术,将当前展示图片的前一张和后一张优先下载。
  3. 雪碧图:如果图片为 css 图片,可以使用 CSSsprite,SVGsprite 等技术。
  4. 缩略图:如果图片过大,可以使用特殊编码的图片,加载时会先加载一张压缩的特别厉害的缩略图,以提高用户体验。

图片懒加载与预加载

图片懒加载的原理就是暂时不设置图片的 src 属性,而是将图片的 url 隐藏起来,比如先写在 data-src 里面,等某些事件触发的时候(比如滚动到底部,点击加载图片)再将图片真实的 url 放进 src 属性里面,从而实现图片的延迟加载

图片预加载,是指在一些需要展示大量图片的网站,实现图片的提前加载。从而提升用户体验。常用的方式有两种,一种是隐藏在 css 的 background 的 url 属性里面,一种是通过 javascript 的 Image 对象设置实例对象的 src 属性实现图片的预加载。相关代码如下: css 实现

#preload-01 {
  background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px;
}
#preload-02 {
  background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px;
}
#preload-03 {
  background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px;
}

Javascript 预加载图片的方式:

function preloadImg(url) {
  var img = new Image();
  img.src = url;
  if (img.complete) {
    //接下来可以使用图片了
    //do something here
  } else {
    img.onload = function () {
      //接下来可以使用图片了
      //do something here
    };
  }
}

预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。 懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。 两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。 懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力

懒加载原理

  1. 路由分割、图片、数据懒加载
  2. 数据日志、error、埋点上报
  3. 日志可视化
  • 懒加载图片
  • 接口、数据懒加载

一开始先给为 src 赋值成一个通用的预览图,下拉时候再动态赋值成正式的图片。如 下, preview.png 是预览图片,比较小,加载很快,而且很多图片都共用这个 preview.png ,加载一次即可。待页面下拉,图片显示出来时,再去替换 src 为 data-realsrc 的值。

<img src="preview.png" data-realsrc="abc.png"/>. 另外,这里为何要用 data- 开头的属性值?—— 所有 HTML 中自定义的属性,都应该用 data- 开头,因为 data- 开头的属性浏览器渲染的时候会忽略掉,提高渲染性能。

  • scrollTop 顶部偏移的值
  • clientHeight 类似 screenHeight
  • scrollHeight 一个元素内容高度的度量,包括由于溢出导致的视图中不可见内容。

scrollTop + clientHeight >= scrollHeight - 100 (阈值) 如果满足的话 就需要执行回调了。

  1. 监听 window 的 scroll 事件, addEventListener 第三个参数是 true 将事件冒泡上来。
  2. 通过一些 api 进行限流操作,比如 lodash 的 throttle, 或者是 requestAnimationXXX ? 等。 高级 API observer IntersectionObserver
  3. 传入一个回调,加载更多的状态下 执行回调。
// 手动计算 可能会卡顿
function isInViewPort(element) {
  const viewWidth = window.innerWidth || document.documentElement.clientWidth;
  const viewHeight =
    window.innerHeight || document.documentElement.clientHeight;
  const { top, right, bottom, left } = element.getBoundingClientRect();

  return top >= 0 && left >= 0 && right <= viewWidth && bottom <= viewHeight;
}

组件懒加载

Vue-LazyLoad

解决的问题

可视区域的图片加载,其他的图片可以暂时有一个占位 loading 图,等滚动它们到可视区域时再去请求真实图片并且替换就好了。

原理简述

  1. lazyload 被安装的时候会创建一个 lazyload 实例,其内部维护了一个 ListenerQueue,实例会通过 add 方法将使用到的队列中。
  2. vue-lazyload 是通过指令的方式实现的,定义的指令是 v-lazy 指令
  3. 指令被 bind 时会创建一个 listener,并将其添加到 listener queue 里面,可以拿到 target 真实 dom 节点,会找它的父元素如果没有指定的话,会一步一步往上找,执行 scrollParent 操作。为其注册 dom 事件(如 scroll 事件)
  4. dom 事件被触发(一般是父级的 scroll 事件),会遍历 listener queue 里的 listener,判断此 listener 绑定的 dom 是否处于页面中 perload 的位置,如果处于则加载异步加载当前图片的资源
  5. 同时 listener 会在当前图片加载的过程的 loading,loaded,error 三种状态触发当前 dom 渲染的函数,分别渲染三种状态下 dom 的内容

简单总结:

  1. 监听父元素容器滚动,触发 scrollHandler 事件
  2. 遍历所有的图片的 dom 元素,且执行 checkInView 方法。
  3. 如果发现在视图范围内的话, 会替换图片的链接。

设计解析

图片懒加载白话原理:监听父元素滚动,遍历所有图片的 dom 元素,并且执行 checkInView 函数,如果在视图范围内,那么就将标签上的 data-src 标签的值替换为 src 的值,进行图片请求。

v-lazy 与 lazy-component 的原理类似。

实现原理

  • 怎么获取一个元素到视图顶部的距离。
  • getBoundingClientRect 获取的 top 和 offsetTop 获取的 top 区别

v-lazy 指令

指令分别有 4 个钩子需要注意,分别是 bind, update, componentUpdated, unbind, 会分别调用 Lazyload 实例中 add, update, lazyLoadHandlerremove 方法。

Lazyload 实例创建完成之后,会在指令对应的节点 bind 的时候,执行 lazyLoad.add 操作,创建一个响应式的 listener ,搜索 target dom (指令对应的 dom),为其注册好对应的 dom 事件之后,比如 scroll, 加入到一个全局的 listenerQueue 队列中去。

checkInView 实现

function checkInView() {
  this.getRect(); // 调用dom的getBoundingClientRect()
  return (
    this.rect.top < window.innerHeight * this.options.preLoad &&
    this.rect.bottom > this.options.preLoadTop &&
    this.rect.left < window.innerWidth * this.options.preLoad &&
    this.rect.right > 0
  );
}

首先看 y 轴方向的判断:this.rect.top < window.innerHeight * this.options.preLoad, 是 dom 的顶部是否到了 preload 的位置;this.rect.bottom > this.options.preLoadTop 判断 dom 的底部是否到达了 preload 的位置

lazyComponent 组件的实现

<lazy-component @show="handler">
  <img class="mini-cover" :src="img.src" width="100%" height="400" />
</lazy-component>

源码分析

  1. 注入 Vue 创建 LazyClass, 传入 options 参数创建 lazy 实例
  2. 将 lazy 实例作为参数初始化 LazyContainer
  3. Vue 原型链上添加 $Lazyload
Last Updated:
Contributors: yiliang114