理論からコードの実践、画像の事前読み込み、遅延読み込みまで

序文

開発プロセスでは、ページの読み込みを高速化し、ユーザーエクスペリエンスを向上させるために、画像をプリロードして遅延ロードすることがよくあります。この2つをいつ使用するかは、状況によって異なります。

プリロード

プリロードとは

事前に画像を読み込み、読み込みが完了した後缓存到本地、ユーザーが画像を表示する必要がある場合は、ローカルキャッシュから直接レンダリングできるため、優れたユーザーエクスペリエンスが得られます。

なぜプリロードを使用するのですか?

用户更好的体验,减少等待的时间この練習は実際に牺牲了服务器的性能换取了更好的用户体验

使用例

専用の画像ウェブサイトのように、最初のオープニングのユーザーエクスペリエンス(長い白い画面ではなく、せっかちなユーザーはページを閉じます)のために、プリロードを使用しますが、すべてがプリロードされているわけではなく、一部がプリロードされていません。ウェブサイトで写真を閲覧していると、この時点で静かに読み込まれる写真もあるので、写真をシームレスに接続する効果が得られ、まったく遅く感じることはありません。

また、写真のグループを閲覧すると、最初の写真だけが表示され、最初の写真を閲覧すると、何らかの形で写真を切り替えることができます(ボタンをクリックします)。実際には、写真はプリロードされています。切り替えられるのを待ってください。

コードプリロード

プリロードを使用する前に

ボタンがクリックされた後、画像が切り替えられて画像が要求され(ユーザーエクスペリエンスが良くない)、画像が実際にサーバーから要求され、キャッシュがない場合に遅延があることがはっきりとわかります。使用されている。

动画.gif

Snipaste_2022-04-07_14-52-57.jpg

プリロードを使用する

スイッチボタンをクリックした後、ページは迅速に応答し、シームレスに接続し(ユーザーエクスペリエンスは優れています)、画像を見ると、ローカルキャッシュを使用したプルリクエストであることがわかります。必要になるまで待ち、キャッシュをヒットし、キャッシュから直接レンダリングします。

动画.gif

Snipaste_2022-04-07_14-54-53.jpg

JSを使用して実装する

<template>
  <div>
    <img :src="imgData[imgInd]" alt="" />
  </div>
  <button @click="onLeft">Left</button>
  <button @click="onRight">Right</button>
</template>

<script lang="ts">
import { ref } from "vue";
export default {
  name: "App",
  setup() {
    let imgData = ref<string[]>([
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/3.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/4.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/5.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/7.jpg",
    ]);
    // 预加载图片
    function preloadImg(srcArr: string[]): void {
      if (srcArr instanceof Array) {
        for (var i = 0; i < srcArr.length; i++) {
          var oImg = new Image();
          oImg.src = srcArr[i];
        }
      }
    }
    preloadImg(imgData.value);

    let imgInd = ref<number>(0);
    function onLeft() {
      imgInd.value - 1 < 0 ? 0 : --imgInd.value;
    }
    function onRight() {
      imgInd.value + 1 == imgData.value.length
        ? imgData.value.length - 1
        : ++imgInd.value;
    }
    return {
      onLeft,
      onRight,
      imgInd,
      imgData,
    };
  },
};
</script>
复制代码

CSSの実装

  <div>
    <img :src="imgData[imgInd]" alt="" />
  </div>
  <button @click="onLeft">Left</button>
  <button @click="onRight">Right</button>
  <div class="img1"></div>
  <div class="img2"></div>
  <div class="img3"></div>
  <div class="img4"></div>
</template>

<script lang="ts">
import { ref } from "vue";
export default {
  name: "App",
  setup() {
    let imgData = ref<string[]>([
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/3.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/4.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/5.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/7.jpg",
    ]);
    
    let imgInd = ref<number>(0);
    function onLeft() {
      imgInd.value - 1 < 0 ? 0 : --imgInd.value;
    }
    function onRight() {
      imgInd.value + 1 == imgData.value.length
        ? imgData.value.length - 1
        : ++imgInd.value;
    }
    return {
      onLeft,
      onRight,
      imgInd,
      imgData,
    };
  },
};
</script>

<style scoped>
.img1 {
  height: 0px;
  width: 0px;
  background: url(https://cdn.jsdelivr.net/gh/lztnb/img@master/3.jpg);
}
.img2 {
  height: 0px;
  width: 0px;
  background: url(https://cdn.jsdelivr.net/gh/lztnb/img@master/4.jpg);
}
.img3 {
  height: 0px;
  width: 0px;
  background: url(https://cdn.jsdelivr.net/gh/lztnb/img@master/5.jpg);
}
.img4 {
  height: 0px;
  width: 0px;
  background: url(https://cdn.jsdelivr.net/gh/lztnb/img@master/5.jpg);
}
</style>
复制代码

遅延読み込み

遅延読み込みとは

遅延読み込みは、遅延読み込みまたは読み込みなしとさえ言えます。占位图ページにアクセスするときは、画像がブラウザの表示領域に表示される場合にのみ、統一された画像パスに置き換えてください(リクエストが一度行われるようにして、サーバーへの負荷を軽減します)。画像の実際のパスが設定され、サーバーが要求されます。

遅延読み込みを使用する理由

懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求ページの読み込み速度が速いため、サーバーへの負荷を軽減し、トラフィックを節約し、優れたユーザーエクスペリエンスを実現できます。

使用例

たとえば、ショッピングモールのページにはたくさんの写真がありますが、一度にロードすると10秒以上出てこないでしょう。したがって、ユーザーインターフェイスに必要な画像が最初に表示され、将来的にページに表示され、将来が到着したときにそれらをロードする必要がある場合があります(プリロードは将来を待たずに直接要求します)。

画像の遅延読み込みを実装するコード

遅延読み込みは使用されません动画.gif

使用懒加载,第一次进页面就快了不少 动画.gif

懒加载代码

原理:图片到最外层offsetTop距离-(图片最近滚动父元素)到最外层offsetTop距离-(图片最近滚动父元素)的scrollTop距离 <= (图片最近滚动父元素)clientHeight距离[+X] (当然可以为了更好的用户体验可以提前加载,再加上一个距离X)。(随便你怎么嵌套滚动,都是可以的,可以在这个基础上再判断父元素是否出现在body的可视范围内。)

看不懂描述没关系,看图

image.png

<template>
  <div>图片懒加载</div>
  <div class="imgFrame">
    <img
      v-for="(data, ind) in imgData"
      :key="ind"
      :src="imgSrcArray[ind]"
      :ref="imgDom"
    />
  </div>
</template>

<script lang="ts">
import { ref } from "vue";
export default {
  name: "App",
  setup() {
    let imgSrcArray = ref<string[]>([]);
    // 要懒加载的数据
    let imgData = ref<string[]>([
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/3.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/4.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/5.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/6.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/7.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/14.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/15.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/16.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/17.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/18.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/19.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/20.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/22.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/23.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/24.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/25.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/26.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/27.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/28.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/29.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/30.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/31.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/32.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/33.jpg",
    ]);
    // 使用默认图片
    imgData.value.forEach((val, ind: number) => {
      imgSrcArray.value[ind] =
        "https://acmphoto.oss-cn-beijing.aliyuncs.com/%E5%8A%A0%E8%BD%BD%E4%B8%AD4_3.png";
    });
    // imgDOM的元素
    let imgDomArray = ref<HTMLElement[]>([]);
    const imgDom = (el: HTMLElement) => {
      imgDomArray.value.push(el);
    };
    // 获取最近可以滚动的父元素
    function getParent<T extends HTMLElement>(e: T): T {
      let parentDom = e.parentNode;
      // eslint-disable-next-line no-constant-condition
      while (true) {
        if (parentDom == document.body) {
          break;
        }
        if ((parentDom as T).clientHeight == (parentDom as T).scrollHeight) {
          parentDom = getParent(parentDom as T);
        } else {
          break;
        }
      }
      return parentDom as T;
    }
    // 得到距离页面最上方的距离
    function getoffsetTop<T extends HTMLElement>(e: T): number {
      let offset = e.offsetTop;
      // 一直递归到最外层
      if (e.offsetParent != null) {
        offset += getoffsetTop(e.offsetParent as T);
      }
      return offset;
    }
    // 节流
    let time: number | null;
    onScroll();
    function onScroll() {
      if (time == null) {
        time = setTimeout(() => {
          onShow();
          time = null;
        }, 500);
      }
    }
    // 判断是否加载
    function onShow(): void {
      for (let i = 0; i < imgSrcArray.value.length; i++) {
        let cHeight = 0;
        let sTop = 0;
        let parentDom = getParent(imgDomArray.value[i]);
        parentDom.addEventListener("scroll", onScroll);
        if (parentDom == document.body) {
          cHeight = document.documentElement.clientHeight as number;
          sTop = document.documentElement.scrollTop as number;
          window.addEventListener("scroll", onScroll);
        } else {
          cHeight = parentDom.clientHeight;
          sTop = parentDom.scrollTop;
          parentDom.addEventListener("scroll", onScroll);
        }
        //判断是否加载的关键
        if (
          getoffsetTop(imgDomArray.value[i]) - getoffsetTop(parentDom) - sTop <=
          cHeight
        ) {
          imgSrcArray.value[i] = imgData.value[i];
        }
      }
    }
    return {
      imgData,
      imgSrcArray,
      imgDom,
    };
  },
};
</script>

<style scoped>
.imgFrame {
  margin-top: 20px;
  width: 800px;
  height: 600px;
  overflow: auto;
}
img {
  height: 100%;
  width: 100%;
  object-fit: contain;
}
</style>

复制代码

结语

通过这样的从理论到实操,应该对图片的预加载和懒加载都有所了解。不过在开发中还是有插件就用插件,这样使开发更快更简单,不用自己写很多代码,说不定还有bug。但是合格的打工人还是要知道原理和会手撸代码,插件可以用,当原理也要会。

おすすめ

転載: juejin.im/post/7084190879140806669