WEB/VUE.js

[VUE.js] vue3 intersectionObserver API 를 사용하여 infinite scroll 구현하기

자바칩 프라푸치노 2021. 11. 18. 11:57

영화리스트가 있는 프로젝트를 만드는데

한번에 다 받아오기 말고 10개씩 끊어서 받아오고 싶은 것이다.

https://developer.mozilla.org/ko/docs/Web/API/Intersection_Observer_API

 

Intersection Observer API - Web API | MDN

Intersection Observer API는 타겟 요소와 상위 요소 또는 최상위 document 의 viewport 사이의 intersection 내의 변화를 비동기적으로 관찰하는 방법입니다.Intersection Observer API는 타겟 요소와 상위 요소 또는

developer.mozilla.org

1. Observer.vue 만들기

이 컴포넌트는 무한 스크롤을 구현하고 싶은 컴포넌트 맨 하단에 붙여서 

이 컴포넌트가 보이면 리스트를 더 불러오는 메소드를 실행하기 위해 만든다.

//Observer.vue

<template>
    <div ref="trigger">-</div>
</template>
<script>
import {
  defineComponent, onMounted, onUnmounted, ref
} from 'vue';

export default defineComponent({
  setup(props, { emit }) {
    const trigger = ref();
    const options = {
      root: null,
      threshold: 1
    };
    let observer = null;
    const handleIntersect = (entry) => {
      if (entry.isIntersecting) emit('triggerIntersected', trigger.value);
    };

    onMounted(() => {
      try {
        observer = new IntersectionObserver((entries) => {
          handleIntersect(entries[0]);
        }, options);
        observer.observe(trigger.value);
      } catch (err) {
        console.error(err);
      }
    });

    onUnmounted(() => {
      observer.value.disconnect();
    });

    return {
      trigger
    };
  }
});
</script>

코드 위에서 부터

1. 보여주는 div를 trigger이라는 ref로 설정해준다.

2. intersection observer에 넣을 옵션을 설정해준다.

threshold는 해당 컴포넌트가 화면에 얼마나 보였을 때를 감지할 것인지를 말하는 것인데 

1은 100%이고 0.1은 10%이다.

3. onMounted에서 observer을 등록한다.

옵저버가 감지되면 handleIntersect이라는 함수를 실행하고

그 함수는 부모로 triggerIntersected 이벤트를 emit으로 보낸다.

4. 그리고 onMounted에서 observer를 연결 해제 시켜준다.

 

 

2. 사용하고 싶은 컴포넌트에서 사용

//Main.vue

<template>
  <div class="filters">
    <Filter
      v-for="(genre) in genres"
      :key="genre.id"
      :genre="genre"
      @click="getGenreMovies(genre.id , genre.name)"
    ></Filter>
  </div>
  <MovieList :movieList="filteredMoviesByGenre"/>
  <Observe @triggerIntersected="loadMore"/>
</template>

나는 Main컴포넌트에서  MovieList가 끝나고 맨 밑에 Observe 컴포넌트를 붙였다.

MovieList가 10개가 다 보이면 Observe 컴포넌트가 보이고

Observe가 보이면 또 영화를 10개 더 가져와서  MovieList에 추가되어 밑으로 밀리는 형태이다.

 

옵저버가 감지됐을때 실행되는 함수는 loadMore함수이다.

<template>
  <div class="filters">
    <Filter
      v-for="(genre) in genres"
      :key="genre.id"
      :genre="genre"
      @click="getGenreMovies(genre.id , genre.name)"
    ></Filter>
  </div>
  <MovieList :movieList="filteredMoviesByGenre"/>
  <Observe @triggerIntersected="loadMore"/>
</template>
<script lang="ts">
import ...
export default defineComponent({
  ...
  setup() {
   ...
    const page = ref(1);

    const loadMore = async () => {
      const fetchData = await getAllMoviesAPI(page.value + 1);
      if (fetchData) {
        await store.dispatch("movie/setMoreMovies", fetchData);
      }
      page.value += 1;
    };
  
  ...

    return {
     ...
      loadMore
    };
  },
});
</script>
<style lang="scss" scoped>
...
</style>

loadMore함수는 page를 1씩 올려서 다음페이지를 가지고 온다.

그리고  store에 저장한다.

 

 

 

3. 결과

 

728x90