在 Custom Elements 中实现懒加载组件的技巧

前言

随着 Web 技术的发展,前端页面中出现了各式各样的组件库,这些组件库在提供便捷的同时,也造成了页面的加载速度变慢问题。因此,实现懒加载组件成为开发过程中重要的一步。本文将介绍如何使用 Custom Elements 实现懒加载组件的技巧。

Custom Elements 简介

Custom Elements 允许开发者自定义 HTML 标签,实现自定义元素的功能。与原生元素不同,自定义元素可以拥有自己的样式和行为,并且能够扩展原生元素的功能。

实现懒加载组件的技巧

指定触发器

懒加载的核心就在于触发加载的时机。一般情况下,我们会通过监听浏览器滚动事件来实现。但是在使用 Custom Elements 实现懒加载组件时,我们可以为懒加载组件指定触发器,即当指定的元素出现在视口时触发加载。

class LazyLoadImg extends HTMLElement {
  constructor() {
    super();
    this.intersectionObserver = null;
  }
  connectedCallback() {
    if (!this.intersectionObserver) {
      this.intersectionObserver = new IntersectionObserver(this.handleIntersection);
    }
    this.intersectionObserver.observe(this.trigger);
  }
  disconnectedCallback() {
    this.intersectionObserver.disconnect();
  }
  get trigger() {
    return this.querySelector(".trigger") || this;
  }
  handleIntersection(entries) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        console.log("Image entered viewport");  // Here we can load images
      }
    });
  }
}

在这段代码中,我们为组件指定一个触发器元素,然后利用 IntersectionObserver 监听触发器元素是否进入视口,并在进入时触发加载图片的代码。

动态添加元素

为了实现懒加载的效果,我们需要动态添加元素。由于 Custom Elements 允许开发者自定义 HTML 标签,因此我们可以借助 Custom Elements 来创建元素。

const img = document.createElement("lz-img");
img.src = "http://example.com/image.jpg";
document.body.appendChild(img);

这段代码创建了一个自定义的 lz-img 元素,并将它添加到了页面上。虽然在大多数浏览器中,自定义元素是自动定义的,但我们也可以显式地自定义元素。

if (!customElements.get("lz-img")) {
  customElements.define("lz-img", LazyLoadImg);
}

在这段代码中,如果 Custom Elements 中还未定义 lz-img 元素,则显式自定义元素并将其定义为 LazyLoadImg 类。

优化加载体验

网络环境较差时,图片的加载速度会变慢,为了提高用户体验,我们可以在图片加载完成前显示一个占位符。

class LazyLoadImg extends HTMLElement {
  constructor() {
    super();
    this.intersectionObserver = null;
  }
  connectedCallback() {
    if (!this.intersectionObserver) {
      this.intersectionObserver = new IntersectionObserver(this.handleIntersection);
    }
    this.intersectionObserver.observe(this.trigger);
  }
  disconnectedCallback() {
    this.intersectionObserver.disconnect();
  }
  get trigger() {
    return this.querySelector(".trigger") || this;
  }
  handleIntersection(entries) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        console.log("Image entered viewport");
        this.loadImage();
      }
    });
  }
  loadImage() {
    const img = new Image();
    img.onload = () => {
      this.innerHTML = "";
      this.appendChild(img);
    };
    img.src = this.dataset.src;
    this.innerHTML = "<div style='aspect-ratio:1/1; background-color:grey;'></div>";
  }
}

在这段代码中,我们为元素指定了 dataset.src 属性,用于存储图片的路径。当触发加载时,我们先创建一个占位符,然后开始加载图片,等图片加载完成后,将占位符替换为图片。

示例

<script type="module">
  class LazyLoadImg extends HTMLElement {
    constructor() {
      super();
      this.intersectionObserver = null;
    }
    connectedCallback() {
      if (!this.intersectionObserver) {
        this.intersectionObserver = new IntersectionObserver(this.handleIntersection.bind(this));
      }
      this.intersectionObserver.observe(this.trigger);
    }
    disconnectedCallback() {
      this.intersectionObserver.disconnect();
    }
    get trigger() {
      return this.querySelector(".trigger") || this;
    }
    handleIntersection(entries) {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          console.log("Image entered viewport");
          this.loadImage();
        }
      });
    }
    loadImage() {
      const img = new Image();
      img.onload = () => {
        this.innerHTML = "";
        this.appendChild(img);
      };
      img.src = this.dataset.src;
      this.innerHTML = "<div style='aspect-ratio:1/1; background-color:grey;'></div>";
    }
  }

  if (!customElements.get("lz-img")) {
    customElements.define("lz-img", LazyLoadImg);
  }
</script>

<lz-img data-src="https://images.unsplash.com/photo-1619337218652-3ca2776ff34c" style="height:300px; width:100%;"></lz-img>
<div style="height: 100vh;"></div>
<lz-img data-src="https://images.unsplash.com/photo-1525253086316-5bf3f5f5e5c5" style="height:300px; width:100%;"></lz-img>
<div style="height: 100vh;"></div>
<lz-img data-src="https://images.unsplash.com/photo-1534498240048-66bb804f64b4" style="height:300px; width:100%;"></lz-img>

以上是一个简单的示例,通过自定义元素 lz-img 来实现图片的懒加载。在 Chrome 中运行效果如下图所示:

总结

本文通过介绍 Custom Elements 的基本用法,并结合懒加载组件的实现,展示了如何借助 Custom Elements 来优化页面的加载性能。在实践中,我们可以根据具体业务场景,针对性地开发自己的懒加载组件。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a22454add4f0e0ffa321ed


纠错反馈