vue 사용자 정의 지시자를 활용한 image lazy loading 구현하기

katanazero·2020년 3월 23일
2

vue

목록 보기
2/15
post-thumbnail

https://velog.io/@katanazero86/Intersection-Observer-API
저번에 IntersectionObserver API 에 대한 글을 작성하였습니다.

오늘은 이 해당 API를 이용하여 vue 에서 image lazy loading 구현을 해보도록 하겠습니다. 😎

사용자 정의 지시자(custom directives)

  • v- 접두사로 시작하는 특수속성
    (v-show, v-model, v-if, v-on, v-bind 등)
  • 기본 지시자 이외에도 사용자 정의 지시자를 등록하여 사용 가능
  • 값 전달, 인자값, modifier 도 사용 가능
// 전역 사용자 정의 디렉티브 v-customfocus 등록
Vue.directive('customfocus', {
  // 바인딩 된 엘리먼트가 DOM에 삽입되었을 때...
  inserted: function (el) {
    // 엘리먼트에 포커스를 줍니다
    el.focus()
  }
})

directives: {
  customfocus: {
    // 디렉티브 정의
    inserted: function (el) {
      el.focus()
    }
  }
}


<input v-customfocus>
  • hook 함수를 제공합니다. hook 함수에는 몇가지 인자값들이 존재
bind : 지시어가 요소에 바인드 될때 1번 호출된다.

inserted : 요소가 부모 DOM에 삽입되면 호출된다.

update : 컴포넌트가 업데이트 되었지만, 하위 컴포넌트가 업데이트 되지 않아도 호출된다.

componentUpdated : 컴포넌트와 하위 컴포넌트가 업데이트 되면 호출된다.

unbind : 바인드가 해제가 되는 경우 호출된다.
  • 인자값
el : 지시어가 바인딩된 엘리먼트 요소
binding : 객체
  - name : 지시어 이름
  - value : 예) v-color="red"  전달받은 값
  - oldValue : 이전값(update, componentUpdated 에서만 사용가능)
  - expression : 표현식 문자열
  - arg : 예) v-sticky:bottom -> "bottom"
  - modifiers : 예) v-format.underline -> { underline: true }
vnode : 가상DOM node
oldVnode : 이전 가상DOM node
Vue.directive("color", function(el, binding, vnode) {
    el.style.color = binding.value;
});

Vue.directive("format", function(el, binding, vnode) {
  const modifiers = binding.modifiers;
  if (modifiers.underline) {
    el.style.textDecoration = "underline";
  }

});

<div v-color="'red'">Show this</div>
<span v-format.underline>guide</span>
  • 위와 같이 정의하여 사용을 하면 된다.

사용자 정의 지시자 생성


// v-lazyload
Vue.directive('lazyload', {
  inserted(el, binding, vnode) {

  },
  
});

IntersectionObserver API 구현

inserted(el) {

    function imageLoad(targetElement) {
      const imgElement = targetElement;
      // data-lazy 에 지정된 이미지 경로를 <img src=""> 에 셋팅 합니다.
      imgElement.setAttribute('src', imgElement.getAttribute('data-lazy'));
      imgElement.onload = function() {
        imgElement.removeAttribute('data-lazy');
      };

    }

    function callIntersectionApi() {

      const options = { 
        root: null, 
        threshold : 0.5, 
        rootMargin: '0px' 
      };
      
      const lazyLoadCallback = (entries, observer) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) { 
          // 감지대상이 교차영역에 진입 할 경우
            imageLoad(entry.target);
            observer.unobserve(entry.target);
          }
        });
      };

      const lazyLoadingIO = new IntersectionObserver(lazyLoadCallback, options);
      lazyLoadingIO.observe(el);

    }

    // 지원되는 브라우저가 있기때문에, 이런식으로 처리
    window.IntersectionObserver ? callIntersectionApi() : imageLoad(el);

  },
  • window.IntersectionObserver ? callIntersectionApi() : imageLoad(el); 검사를 통해, API 지원여부를 확인합니다. 지원하지 않는다면 바로 이미지를 불러옵니다.

  • API 지원시, IntersectionObserver callback 함수 작성 및 option 객체 작성 후 인스턴스를 생성해줍니다. 인스턴스 생성 후 observe() 를 호출하여 관찰대상 엘리먼트를 지정합니다.

  • 교차영역에 관찰이 되어 callback 이 실행이 되면, isIntersecting 속성을 통해 검사 후 imageLoad() 를 호출합니다. 그리고 해당 엘리먼트에 대한 감시를 해제해줍니다.

<img :data-lazy="info.imgUrl" v-lazyload>
  • 초기에 이미지를 셋팅할때, 위와같이 구성합니다. 이미지가 실제 안보이는 영역에는 랜더링이 되어있지 않기 때문에 성능이 좋습니다.

  • data-lazy 속성을 읽어서, src에 지정해주고 onload가 완료되면 해당 속성을 삭제해줍니다.

profile
developer life started : 2016.06.07 ~ 흔한 Front-end 개발자

2개의 댓글

comment-user-thumbnail
2021년 10월 24일

덕분에 좋은 정보 알게되었습니다. 감사합니다!

1개의 답글