1. Vue project 만들기

# 뷰가 설치되어 있지 않다면 설치합니다.
$ yarn global add @vue/cli

$ vue create vue-lazy-loading
$ cd vue-lazy-loading

# 서버 실행
$ yarn serve or npm run serve

스크린샷 2019-08-29 오후 11.43.31.png

2. 이미지 리스트 컴포넌트 생성

  • ImageList.vue 생성
  • ImageItem.vue 생성

기본 세팅 Code 보기

### App.vue
<template>
  <div id="app">
    <ImageList />
  </div>
</template>

<script>
import ImageList from './components/ImageList.vue'

export default {
  name: 'app',
  components: {
    ImageList,
  }
}
</script>

<style>

</style>

### ImageList.vue
<template>
  <div class="img-list">
      <ImageItem />
  </div>
</template>

<script>
import ImageItem from './ImageItem'

export default {
  name: 'ImageList',
  components: {
      ImageItem,
  },
  props: {
    msg: String
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>

### ImageItem.vue
<template>
  <div class="img-item">
      이미지 아이템 
  </div>
</template>

<script>
export default {
  name: 'ImageItem',
  props: {
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>

3. 이미지 리스트 렌더링 해보기

  • 이미지를 보여줄려면 이미지가 필요하겠죠?
    https://picsum.photos/
    위 사이트 랜덤 이미지 URL을 활용 해보겠습니다.
### ImageItem.vue
<template>
  <div class="img-item">
      <img :src="imgSrc" /> <!-- 전달 받은 이미지 URL을 바인딩 -->
  </div>
</template>

<script>
export default {
  name: 'ImageItem',
  props: {
      imgSrc: String, // 부모 컴포넌트 이미지 URL props를 전달 받기위해 추가합니다
  }
}
</script>

### ImageList.vue
<template>
  <div class="img-list">
      <ImageItem img-src="https://picsum.photos/id/1/600/600" /> <!-- 이미지 URL을 전달합니다 -->
  </div>
</template>

<script>
import ImageItem from './ImageItem'

export default {
  name: 'ImageList',
  components: {
      ImageItem,
  },
}
</script>

스크린샷 2019-08-30 오전 12.16.30.png

4. 사용자 지정 디렉티브 만들기

  • ./src/directives/CustomDirective.js
  • ./src/directives/Index.js
  • vue directive에 대해서는 공식 사이트를 참고 해주세요
    https://kr.vuejs.org/v2/guide/custom-directive.html
  • 보통 저같은 경우는 Element에 접근할때 Directive를 만들어서 사용해요
### ./src/directives/CustomDirective.js

import Vue from 'vue';

const ImgLazyLoading = {
    inserted(el, binding) {
        console.log('directive inserted');
    },
    unbind(el, binding) {

    },
};

Vue.directive('img-lazy-loading', ImgLazyLoading);

### ./src/directives/Index.js

require('./CustomDirective');


### ./src/main.js

import Vue from 'vue'
import App from './App.vue'

require('./directives'); // directives 모듈 추가

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

### ./src/components/ImageItem.vue
- img 태그에 v-img-lazy-loading 디렉티브 속성을 추가
<template>
  <div class="img-item">
      <img :src="imgSrc" v-img-lazy-loading /> <!-- 전달 받은 이미지 URL을 바인딩 -->

스크린샷 2019-08-30 오전 12.37.25.png

  • 로그가 찍히는걸보니 잘 작동되고 하네요~

4. 100개 이미지 아이템 리스트 만들기

  • Lazy Loading을 구현하기 앞서 리스트를 만들어야겠죠?
  • v-for 디렉티브를 사용해서 구현합니다
### ./src/components/ImageList.vue

<template>
  <div class="img-list">
      <ImageItem  v-for="n in 100" :img-src="'https://picsum.photos/id/'+n+'/600/600'"  :key="n" />

5. Lazy Load Directive 코딩

### ./src/directives/CustomDirective.js

import Vue from 'vue';

const ImgLazyLoading = {
    inserted(el, binding) {
        let options = {
            root: document.querySelector(binding.value.rootScrollEl), // scroll event를 감시할 element를 설정 합니다
            rootMargin: "500px 0px" 
        };

        // 이미지 로드 되기전에 placeholder를 보여주기 위해 element를 생성합니다
        var placeholderEl = document.createElement("div");  
        placeholderEl.setAttribute("style", "position: absolute; top: 0; left: 0; right: 0; bottom:0; background-color: #ccc;transition : opacity 1s ease-in-out;");

        el.appendChild(placeholderEl); 

        // 감시
        el.observer = new IntersectionObserver(changes => {
            for (const change of changes) {
                // element가 노출 되는것을 검사합니다
                if(change.intersectionRatio > 0) {
                    // 노출이 되면 옵저버를 해제합니다
                    el.observer.disconnect();

                    var imgEl = document.createElement("img");  
                    imgEl.setAttribute("src", binding.value);

                    // 이미지 로드가 되면 img태그를 append합니다
                    // placeholder는 제거해줍니다.
                    imgEl.onload = function(){
                        console.log('img onload : ' + binding.value);
                        placeholderEl.style["opacity"] = 0;
                        el.appendChild(imgEl); 
                        setTimeout(() => {
                            placeholderEl.remove();
                        },600)
                    };
                }
            }
        }, options);

        el.observer.observe(el);
    },
    unbind(el, binding) {
        if(el.observer) {
            el.observer.disconnect();
        }
    },
};

Vue.directive('img-lazy-loading', ImgLazyLoading)

마무리

끝.

결과물 : https://drive.google.com/open?id=1gUA1e0qLkHez7pBQPYFptwSfmSebyRpm

github : https://github.com/KTH12/vue-lazy-loading-example