# 뷰가 설치되어 있지 않다면 설치합니다.
$ yarn global add @vue/cli
$ vue create vue-lazy-loading
$ cd vue-lazy-loading
# 서버 실행
$ yarn serve or npm run serve
### 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>
### 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>
### ./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을 바인딩 -->
Lazy Loading을 구현하기 앞서 리스트를 만들어야겠죠?
v-for 디렉티브를 사용해서 구현합니다
IntersectionObserver API를 사용합니다.
Element가 얼만큼 노출됬는지를 감시할수있습니다.
참고(https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)
### ./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