웹 구성 요소를 위한 IntersectionObserver 믹스인

DOM에 추가된 요소의 성능을 향상시키기 위해 사용하는 "하나의 이상한 트릭"은 조건부 렌더링입니다. 먼저 믹스인 접근 방식으로 시작하여 조건부/지연 렌더링에 사용하는 몇 가지 다른 방법을 다룰 것입니다.

IntersectionObserver이 무엇인지 모르는 경우 기본적으로 무언가가 표시될 때 이벤트를 생성할 수 있는 낮은 수준의 자바스크립트 API입니다. 즉, 사용자가 앱에서 아래로 스크롤하여 요소를 "보는"이벤트에 응답할 수 있습니다. 요소가 표시될 때를 알면 성능 최적화를 위해 요소의 전체 내부를 조건부로 로드할 수 있습니다!

LitElement의 사용을 장려하는 동안 IntersectionObserverMixin은 모든 웹 구성 요소와 함께 작동하는 바닐라JS 믹스인입니다. 먼저 무엇을 하는지 살펴보고 다른 예를 살펴보겠습니다. 모두 거의 동일합니다.

TLDWTRTC(너무 길어서 코드를 읽고 싶지 않음)


  • new IntersectionObserver를 지원하는 SuperClass를 내보냅니다.
  • 여기에는 constructor에 적용되는 몇 가지 합리적인 기본값이 있으며 구현에서 변경할 수 있습니다
  • .
  • connectedCallback 우리는 관찰자를 활성화합니다
  • disconnectedCallback 우리는 관찰자의 연결을 끊습니다
  • LitElement 관련: elementVisible라는 반응 속성이 있습니다.

  • 코드를 보여주세요(또는 메모리를 지연 로드하는 경우 건너뛰기)




    const IntersectionObserverMixin = function (SuperClass) {
      // SuperClass so we can write any web component library / base class
      return class extends SuperClass {
        /**
         * Constructor
         */
        constructor() {
          super();
          // listen for this to be true in your element
          this.elementVisible = false;
          // threasholds to check for, every 25%
          this.IOThresholds = [0.0, 0.25, 0.5, 0.75, 1.0];
          // margin from root element
          this.IORootMargin = "0px";
          // wait till at least 50% of the item is visible to claim visible
          this.IOVisibleLimit = 0.5;
          // drop the observer once we are visible
          this.IORemoveOnVisible = true;
          // delay in observing, performance reasons for minimum at 100
          this.IODelay = 100;
        }
        /**
         * Properties, LitElement format
         */
        static get properties() {
          let props = {};
          if (super.properties) {
            props = super.properties;
          }
          return {
            ...props,
            elementVisible: {
              type: Boolean,
              attribute: "element-visible",
              reflect: true,
            },
          };
        }
        /**
         * HTMLElement specification
         */
        connectedCallback() {
          if (super.connectedCallback) {
            super.connectedCallback();
          }
          // setup the intersection observer, only if we are not visible
          if (!this.elementVisible) {
            this.intersectionObserver = new IntersectionObserver(
              this.handleIntersectionCallback.bind(this),
              {
                root: document.rootElement,
                rootMargin: this.IORootMargin,
                threshold: this.IOThresholds,
                delay: this.IODelay,
              }
            );
            this.intersectionObserver.observe(this);
          }
        }
        /**
         * HTMLElement specification
         */
        disconnectedCallback() {
          // if we have an intersection observer, disconnect it
          if (this.intersectionObserver) {
            this.intersectionObserver.disconnect();
          }
          if (super.disconnectedCallback) {
            super.disconnectedCallback();
          }
        }
        /**
         * Very basic IntersectionObserver callback which will set elementVisible to true
         */
        handleIntersectionCallback(entries) {
          for (let entry of entries) {
            let ratio = Number(entry.intersectionRatio).toFixed(2);
            // ensure ratio is higher than our limit before trigger visibility
            if (ratio >= this.IOVisibleLimit) {
              this.elementVisible = true;
              // remove the observer if we've reached our target of being visible
              if (this.IORemoveOnVisible) {
                this.intersectionObserver.disconnect();
              }
            }
          }
        }
      };
    };
    
    export { IntersectionObserverMixin };
    


    실제 사용



    따라서 프로덕션에서 이것을 활용할 때 다음과 같은 타이밍을 얻습니다.
  • 요소 정의 로드(일반적으로 이전 게시물의 동적import() 사용)
  • render() 요소의 내용을 조건부로 렌더링하기 위해 요소의 ${this.elementVisible ? html 삼항 문(LitElement 특정)을 사용합니다.
  • :가 true로 변경되면 요소
  • 의 그림자에 사용된 내부 요소의 동적}을 추가로 수행하기 위해 ${this.elementVisibile} 수명 주기 콜백(LitElement 관련)을 적용합니다.
  • 요소가 표시되면 기본적으로 IntersectionObserver의 연결을 끊습니다
  • .

    이 접근 방식은 최소한의 노력으로 성능을 최대화합니다. 또한 HAX의 콘텐츠 작성자가 updated() 또는 import() 또는 video-player와 같은 요소를 페이지에 추가하고 성능에 대해 생각할 필요가 없지만 CMS 기반 사용자 생성 콘텐츠에 대해 합리적으로 높은 점수를 얻을 수 있습니다.

    구현 방식



    기본적으로 두세 가지가 있으며 단일 종속성을 가진 더 빠른 요소가 있습니다. 이 예는 wikipedia-query에서 가져온 것입니다.
  • 가져오기:

  • import { IntersectionObserverMixin } from "@lrnwebcomponents/intersection-element/lib/IntersectionObserverMixin.js";
    


  • 조건부 렌더링:

  • // LitElement render function
      render() {
        return html` ${this.elementVisible
          ? html` <h3 .hidden="${this.hideTitle}" part="heading-3">
                ${this._title}
              </h3>
              <div id="result"></div>
              <citation-element
                creator="{Wikipedia contributors}"
                scope="sibling"
                license="by-sa"
                title="${this.search} --- {Wikipedia}{,} The Free Encyclopedia"
                source="https://${this
                  .language}.wikipedia.org/w/index.php?title=${this.search}"
                date="${this.__now}"
              ></citation-element>`
          : ``}`;
      }
    


  • 내부적으로 조건부 논리에 대한 변수를 조건부로 가져오거나 사용합니다.

  • updated(changedProperties) {
        changedProperties.forEach((oldValue, propName) => {
          // element is visible, now we can search
          if (propName == "elementVisible" && this[propName]) {
            import("@lrnwebcomponents/citation-element/citation-element.js");
          }
          if (
            ["elementVisible", "search", "headers", "language"].includes(
              propName
            ) &&
            this.search &&
            this.headers &&
            this.elementVisible &&
            this.language
          ) {
            clearTimeout(this._debounce);
            this._debounce = setTimeout(() => {
              this.updateArticle(this.search, this.headers, this.language);
            }, 10);
          }
        });
      }
    


    여기에서 우리는 wikipedia-query 용어와 함께 a11y-gif-player 용어, wikipedia-queryelementVisibile를 사용하여 위키피디아 공개 API의 search를 트리거한다는 점에서 language에 대한 추가 조건부 논리를 볼 수 있습니다. 즉, 웹사이트 하단에 wikipedia 기사가 포함되어 있으면 사용자가 해당 기사가 뷰포트에 표시될 때까지 API를 호출하여 기사를 로드하지 않습니다.

    요소의 성능을 끌어내기 위해 이 IntersectionObserverMixin을 구현한 다른 코드 예제:
  • accent-card
  • a11y-gif-player
  • github-preview
  • video-player

  • 이것은 구현이 존재하지 않지만 변경에 대한 속성을 관찰하여 VanillaJS 구현과 함께 사용할 수도 있습니다. 또한 지연 로딩 이미지 자리 표시자 SVG를 수행하기 위해 IntersectionObserverMixin을 혼합하는 SuperClass인 lazyImageLoader에서 보다 고급 구현을 찾을 수 있습니다.

    좋은 웹페이지 즐겨찾기