# Lazy loading image trong vue với lozad

Trong 3 bài viết về optimize image, mình đã hướng đến việc xử lý ảnh trước khi đưa vào trang web. Trong bài viết này chúng ta sẽ tiến đến việc làm thế nào để load image.

Lazy load => load lười biếng => load kiểu nước đến chân mới nhảy => khi nào cần hiển thị mới load.

Hiểu đơn gỉản là khi nào ảnh nằm trong phần hiển thị cho người dùng xem thì trình duyệt mới load ảnh đấy về. Có 1 thư viện khá nhẹ (mình ưu tiên load nhẹ và đơn giản 😄) giúp ích cho chúng ta là lozad (opens new window) 😄 Đây là một thư viện khá gọn nhẹ và tiện dụng đủ dùng 😄

# lozad.js

Lozad là một thư viện sử dụng IntersectionObserver API (opens new window) và javascript (pure js) để lazy load.

Đây là ví dụ dùng lozad:

const el = document.querySelector('img');
const observer = lozad(el);
observer.observe();

Với custom options:

const el = document.querySelector('img');
const observer = lozad(el, {
    rootMargin: '10px 0px',
    threshold: 0.1
});
observer.observe();

Giống với việc sử dụng IntersectionObserver, khi sử dụng lozad chúng ta sẽ cần truyền element cần theo dõi, rootMargin, threshold.

  • root: element chúng ta cần lazy load.
  • rootMargin: giống với thuộc tính margin trong css, như trong ví dụ trên thì khi viewport cách root element 10px về phía trên thì load function được gọi. Mặc định là 0px.
  • threshold: là 1 số hoặc array có giá trị trong khoảng [0..1]. Ví dụ threshold là 0.5 thì khi 1 nửa root element nằm trong viewport thì load function được gọi. Mặc định là 0.

# Sử dụng lozad trong vue component

Để sử dụng trong vue component ta có thể đơn giản chỉ cần như sau:

mounted() {
  const observer = lozad(this.$el);
  observer.observe();
}

Tuy nhiên mình muốn khi ảnh chưa hiện thì ta sẽ hiện ra 1 ảnh mở đến khi ảnh thật load xong chúng ta sẽ hiển thị ảnh thật. Vì thế mình sẽ viết thêm một chút để custom lại.

<template>
  <img
    :class="imgClass"
    :data-src="lazySrc"
    :data-srcset="lazySrcset"
    :style="style"/>
</template>

<script>
import lozad from "lozad";

export default {
  name: "LazyImage",
  props: {
    imgClass: {
      type: String,
      required: false,
      default: ''
    },
    backgroundColor: {
      type: String,
      required: false,
      default: "#efefef"
    },
    width: {
      type: Number,
      required: false,
      default: null
    },
    height: {
      type: Number,
      required: false,
      default: null
    },
    lazySrc: {
      type: String,
      required: true
    },
    lazySrcset: {
      type: String,
      required: false,
      default: null
    }
  },
  data() {
    return {
      loading: true
    };
  },
  computed: {
    aspectRatio() {
      if (!this.width || !this.height) return null;
      return (this.height / this.width) * 100;
    },
    style() {
      const style = { backgroundColor: this.backgroundColor };
      if (this.width) style.width = `${this.width}px`;
      const applyAspectRatio = this.loading && this.aspectRatio;
      if (applyAspectRatio) {
        style.height = 0;
        style.paddingTop = `${this.aspectRatio}%`;
      }
      return style;
    }
  },
  mounted() {
    const setLoadingState = () => {
      this.loading = false;
    };
    this.$el.addEventListener("load", setLoadingState);
    this.$once("hook:destroyed", () => {
      this.$el.removeEventListener("load", setLoadingState);
    });
    const observer = lozad(this.$el);
    observer.observe();
  }
};
</script>

Cám ơn các bạn đã đọc 😄 Chúng ta sẽ tiếp tục với lazy load component ở bài sau.

Bài viết tham khảo từ bài viết của tác gỉả Markus Oberlehner : https://markus.oberlehner.net/blog/lazy-loading-responsive-images-with-vue/