Intersection Observer API를 사용하여 무한 스크롤 구성 요소 빌드
업데이트: Link
Intersection Observer API를 사용하여 무한 스크롤 구성 요소 빌드
https://vueschool.io/articles/vuejs-tutorials/build-an-infinite-scroll-component-using-intersection-observer-api/
Written by Alex Jover Morales
애플리케이션을 구축할 때 사용자를 위한 목록이나 검색 인터페이스를 디자인해야 하는 경우를 종종 접하게 됩니다. 그들은 일반적으로 많은 데이터를 관리하므로 사용자가 “chunks“로 표시하여 애플리케이션 성능과 데이터를 정리할 수 있는 방법이 필요합니다.
아마도 페이지 매김 구성 요소를 구축했을 것입니다. 그들은 구조화된 데이터를 탐색하는 좋은 방법을 제공합니다. 페이지를 쉽게 건너뛸 수 있으며 때로는 여러 페이지를 건너뛸 수 있습니다.
무한 스크롤은 특히 모바일 및 터치 가능한 장치에서 더 나은 UX를 제공할 수 있는 페이지 매김 구성 요소에 대한 좋은 대안입니다. 사용자가 페이지를 스크롤할 때 투명한 페이지 매김을 제공하여 끝이 없는 목록을 탐색하는 느낌을 줍니다.
Intersection Observer API 가 브라우저에 상륙한 이후로 무한 스크롤 구성 요소를 구축하는 것이 결코 쉬운 일이 아닙니다.
빌드하는 방법을 살펴보겠습니다.
Intersection Observer API
Intersection Observer API는 요소가 뷰포트에 들어갈 때 알림을 받기 위해 관찰할 수 있는 구독 가능한 모델을 제공합니다.
교차 관찰자 인스턴스를 만드는 것은 쉽습니다.
IntersectionObserver의 새 인스턴스를 만들고 observe 메서드를 호출하여 DOM 요소를 전달하기만 하면 됩니다.
const observer = new IntersectionObserver();
const coolElement = document.querySelector("#coolElement");
observer.observe(coolElement);
그러나 coolElement가 뷰포트에 들어갈 때 관찰자가 알림을 받는 방법은 무엇입니까?
IntersectionObserver 생성자는 이를 위해 사용할 수 있는 인수로 콜백을 허용합니다.
const observer = new IntersectionObserver(entries => {
const firstEntry = entries[0];
if (firstEntry.isIntersecting) {
// Handle intersection here...
}
});
const coolDiv = document.querySelector("#coolDiv");
observer.observe(coolDiv);
보시다시피 콜백은 entries를 인수로 받습니다.
thresholds 를 사용할 때 여러 항목을 가질 수 있기 때문에 배열이지만,
그렇지 않으므로 첫 번째 요소만 가져옵니다.
그런 다음 firstEntry.isIntersection 속성을 사용하여 교차 여부를 확인할 수 있습니다.
그것은 ajax 호출을 하고 다음 데이터 페이지를 검색하기에 좋은 곳입니다.
IntersectionObserver 생성자는 다음 표기법을 사용하여 두 번째 인수로 옵션 구성 요소를 받습니다.
const options = {
root: document.querySelector("#scrollArea"),
rootMargin: "0px",
threshold: 1.0
};
const observer = new IntersectionObserver(callback, options);
rootMargin은 관찰자가 교차점을 찾는 데 사용할 여백을 정의하는 방법을 제공하기 때문에 우리의 경우에 유용할 수 있습니다.
default 0입니다. 즉, 관찰자는 뷰포트에 들어가자마자 교차 이벤트를 트리거합니다.
그러나 rootMargin을 400px로 설정하면 요소가 뷰포트에 들어가기 전에 교차 콜백이 정확히 400px를 트리거합니다.
루트와 임계값은 이 경우에 그다지 의미가 없기 때문에(따라서 범위를 벗어남) 문서에서 이에 대해 조사하는 것은 사용자에게 맡깁니다.
Intersection Observer를 사용하는 방법을 알면 사용자가 목록의 맨 아래에 도달했을 때 더 많은 데이터를 추가하기 위해 목록 끝에 Observable 구성 요소를 배치하는 기술을 사용할 수 있습니다.
Observer Component
앞의 예가 멋지죠? 그러나 Vue 앱에서 사용할 수 있도록 Vue 구성 요소가 있는 것이 편리합니다.
mounted 후크를 사용하여 구성 요소 상태 변수에 보관해야 하는 관찰자를 설정할 수 있습니다.
관찰할 DOM 요소가 필요하고 created 후크에는 이 요소가 없기 때문에 created 대신 mounted 후크를 사용하는 것이 중요합니다.
// Observer.vue
export default {
data: () => ({
observer: null
}),
mounted() {
this.observer = new IntersectionObserver(([entry]) => {
if (entry && entry.isIntersecting) {
// ...
}
});
this.observer.observe(this.$el);
}
};
tip entry 인수에 대해 배열 분해를 사용하고 있습니다.
이는 항목 배열을 가져오고 첫 번째 요소에 entries[0]로 액세스하는 것과 동일한 약식 방법입니다.
보시다시피 컴포넌트의 루트 요소인 this.$el을 관찰 가능한 DOM 요소로 사용하고 있습니다.
재사용 가능하게 하려면 부모 구성 요소(Observer 구성 요소를 사용하는 구성 요소)가 교차된 이벤트를 처리하도록 해야 합니다.
이를 위해 교차할 때 커스텀 이벤트 intersect를 생성할 수 있습니다.
export default {
mounted() {
this.observer = new IntersectionObserver(([entry]) => {
if (entry && entry.isIntersecting) {
this.$emit("intersect");
}
});
this.observer.observe(this.$el);
}
// ...
};
구성 요소의 템플릿에 따라 요소가 필요하므로 dimensions 없이 <div>를 사용할 수 있습니다.
<template>
<div class="observer"/>
</template>
마지막으로 구성 요소가 파괴될 때 관찰자를 정리하는 것이 중요합니다.
그렇지 않으면 이벤트 수신기가 정리되지 않기 때문에 애플리케이션에 메모리 누수가 추가됩니다.
관찰자 disconnect 메소드를 호출하기 위해 destroyed 후크를 사용할 수 있습니다.
export default {
destroyed() {
this.observer.disconnect();
}
// ...
};
unobserve 메소드도 찾아 내게 될 것입니다. 주요 차이점은 다음과 같습니다.
unobserve: 요소 관찰을 중지합니다.disconnect: 모든 요소 관찰을 중지합니다.
이 경우 요소가 하나만 있으므로 둘 다 작동합니다.
rootMargin을 사용하려는 경우에 대비하여 Intersection Observer 옵션을 전달하기 위해 options 속성을 추가할 수도 있습니다.
Observer.vue 구성 요소에 모두 합치기:
<!-- Observer.vue -->
<template>
<div class="observer"/>
</template>
<script>
export default {
props: ['options'],
data: () => ({
observer: null,
}),
mounted() {
const options = this.options || {};
this.observer = new IntersectionObserver(([entry]) => {
if (entry && entry.isIntersecting) {
this.$emit("intersect");
}
}, options);
this.observer.observe(this.$el);
},
destroyed() {
this.observer.disconnect();
},
};
</script>
Infinite Scroll 만들기
다음과 유사한 목록 구성 요소가 있다고 상상해보십시오.
<template>
<div>
<ul>
<li class="list-item" v-for="item in items" :key="item.id">
</li>
</ul>
</div>
</template>
<script>
export default {
data: () => ({ items: [] }),
async mounted() {
const res = await fetch("https://jsonplaceholder.typicode.com/comment");
this.items = await res.json();
}
};
</script>
tip 이 코드는 async/await 최신 구문을 사용하여 비동기 코드를 보기 좋게 만듭니다. 자세한 내용은 문서 를 참조하세요.
이 구성 요소에는 v-for를 사용하여 목록으로 렌더링된 items 상태 변수가 있습니다.
mounted 후크에서 jsonplaceholder.typicode.com에서 일부 모의 데이터를 가져오기 위해 Fetch API를 사용합니다.
이 데이터는 items 변수를 채우는 데 사용됩니다.
Adding pagination
멋지고 작동하겠지만 아직 어떤 종류의 페이지 매김도 사용하지 않습니다.
이를 위해 jsonplaceholder.typicode.com 의 엔드포인트를 통해 반환되는 데이터를 제어하기 위해 _page 및 _limit를 사용할 수 있습니다.
또한 1부터 시작하는 페이지 변수를 추적해야 합니다.
페이지 매김을 적용하기 위해 이러한 변경 사항을 적용해 보겠습니다.
export default {
data: () => ({ page: 1, items: [] }),
async mounted() {
const res = await fetch(
`https://jsonplaceholder.typicode.com/comments?_page=${this.page}&_limit=50`
);
this.items = await res.json();
}
};
이제 페이지당 요소가 50개인 페이지가 있습니다.
Observer component 추가하기
무한 스크롤 구성 요소를 만들기 위한 최종 구성 요소인 Observer component가 여전히 필요합니다.
뷰포트에 도달하면 다음 페이지를 가져와서 페이지를 늘리는 방식으로 목록 맨 아래에서 사용합니다.
먼저 Observer 구성 요소를 가져와 목록 바로 아래에 있는 InfiniteScroll 구성 요소에 추가합니다.
<template>
<div>
<ul>
<li class="list-item" v-for="item in items" :key="item.id"></li>
</ul>
<Observer @intersect="intersected"/>
</div>
</template>
<script>
import Observer from "./Observer";
export default {
// ...
components: {
Observer
}
};
</script>
마지막으로 mounted 후크에 있는 코드를 Observer 구성요소의 intersect 사용자 정의 이벤트에서 호출되는 intersected 메소드로 이동할 수 있습니다.
export default {
data: () => ({ page: 1, items: [] }),
methods: {
async intersected() {
const res = await fetch(
`https://jsonplaceholder.typicode.com/comments?_page=${this.page}&_limit=50`
);
this.page++;
const items = await res.json();
this.items = [...this.items, ...items];
}
}
};
페이지를 늘려야 함을 명심하십시오.
또한 이제 기존 this.items 배열에 항목을 추가해야 합니다.
this.items = [...this.items, ...items] 줄에 스프레드 연산자를 사용하여 이를 수행합니다.
이는 기본적으로 이전 방식의 this.items = this.items.concat(items)와 동일합니다.
Infinite scroll component를 모두 함께:
<!-- InfiniteScroll.vue -->
<template>
<div>
<ul>
<li class="list-item" v-for="item in items" :key="item.id"></li>
</ul>
<Observer @intersect="intersected"/>
</div>
</template>
<script>
import Observer from "./Observer";
export default {
data: () => ({ page: 1, items: [] }),
methods: {
async intersected() {
const res = await fetch(`https://jsonplaceholder.typicode.com/comments?_page=${this.page}&_limit=50`);
this.page++;
const items = await res.json();
this.items = [...this.items, ...items];
},
},
components: {
Observer,
},
};
</script>
마무리
무한 스크롤 구성 요소는 특히 모바일 및 터치 가능한 장치에서 데이터를 페이징하는 좋은 방법입니다. Intersection Observer API가 추가되어 훨씬 쉬워졌습니다. 이 기사에서는 직접 구축하는 데 필요한 모든 단계를 거쳤습니다.
구식 브라우저에 대한 지원이 필요한 경우 W3C’s Intersection Observer 및 Github’s fetch 폴리필이 필요할 수 있음을 명심하십시오.
Codesandbox 에서 작동하는 이 코드의 데모를 볼 수 있습니다.
댓글남기기