Skip to content
大纲

图片懒加载

图片懒加载,即当页面滚动到图片可视区或指定一段区域时再发送请求加载图片。

主要思路

根据 Element.getBoundingClientRect() 获取元素相对于视口的位置,
获取相对视口左上角计算的 top, left, right, bottomxy 等信息
top 小于视口高度或指定距离时即可进行图片加载
文档视口高度可以根据 document.documentElement.clientHeight 获取

插件实现

这里根据上述思路封装一个更为通用的 vue 指令

ts
import Vue, { DirectiveOptions } from "vue";

interface Manage {
  visible: boolean; // 元素是否在可视区
  every: boolean; // 是否每次从 hide -> visible 都触发
  triggered: boolean; // 是否曾触发过
  handler: Function;
}

const cache = { clientHeight: document.documentElement.clientHeight };
const isFn = (v: unknown): v is Function => typeof v === "function";
const store: Map<HTMLElement, Manage> = new Map();
const updateCache = () =>
  (cache.clientHeight = document.documentElement.clientHeight);
const isVisible = (ele: HTMLElement) => {
  const { top, bottom } = ele.getBoundingClientRect();
  return top < cache.clientHeight && bottom > 0;
};
const listenScroll = () => {
  for (const item of store) {
    const [ele, manage] = item;
    const visible = isVisible(ele);
    if (manage.visible != visible) {
      manage.visible = visible;
      if (visible && (manage.every || !manage.triggered)) {
        manage.handler(ele);
        manage.triggered = true;
      }
    }
  }
};
window.addEventListener("scroll", listenScroll);
window.addEventListener("resize", updateCache);
const visibleConfig: DirectiveOptions = {
  bind(ele, { value, modifiers }) {
    if (isFn(value)) {
      // 确保 dom 渲染
      Vue.nextTick(() => {
        const visible = isVisible(ele);
        const manage = {
          visible,
          every: modifiers.every || false,
          triggered: false,
          handler: value as Function,
        };
        if (manage.visible) {
          value(ele);
          manage.triggered = true;
        }
        store.set(ele, manage);
      });
    } else {
      throw new TypeError("v-visible accept a function");
    }
  },
  unbind(ele, { value, modifiers }) {
    store.delete(ele);
  },
};

export default visibleConfig;

其他方式

针对 img 标签,高版本的浏览器提供了 loading="lazy" 属性,其在距离可视区一点距离时自动加载图片,如

html
<img src="https://some.image.com" loaing="lazy" />

针对更为通用的元素,提供了 IntersectionObserver API(实验阶段),用于观察元素是否出现在可视区,如

js
const target = document.getElementById("target");
const observer = new IntersectionObserver(function (entries) {
  if (entries[0].intersectionRatio <= 0) return; // intersectionRatio 为0表示目标在可视区外
  console.log("进入可视区了");
});
observer.observe(target);