본문 바로가기
프로젝트/트러블슈팅

Vue2 에서 Suspense 를 쓰고싶어졌다

by nr2p 2023. 11. 17.
반응형

회사(여긴 react) 다니면서도 딱히 사용하지 않았던 Suspense 였지만 쓰고 싶어졌다.

물론 Vue3 에서는 이미 구현되어있지만 내 프로젝트는 Vue2 이기 때문에 직접 구현했다.


Vue2 에서 Suspense 사용하기

const Suspense = Vue.extend({
  name: 'Suspense',
  data() {
    return {
      isPageLoading: false,
    };
  },
  async created() {
    this.isPageLoading = true;
    const getPageData = (this.$parent as any).getPageData;

    if (!getPageData) {
      throw new Error('getPageData methods 가 존재하지 않습니다');
    }

    await getPageData();
    this.isPageLoading = false;
  },
  render(createElement) {
    return createElement('div', this.isPageLoading ? this.$slots.fallback : this.$slots.default);
  },
});

 

여기서는 this.$parent 아래의 setup 을 찾아서 하지만 내가 했을 때는 setup 을 찾을 수가 없었고

복잡한 사정에 의해 컴포지션 api 를 사용하지 말아야하는 이유 때문에 위와 같이 구현했다.

 

getPageData > page 정보를 불러오는 데이터

isPageLoading > page 로딩 여부

 

에러처리는 각 컴포넌트의 getPageData 내부에서 하는 걸로 결정했다.. 

물론 에러바운더리 같은걸 만들면서 마음이 바뀔 수도 있지만..ㅎ

 

아무튼 위와 같이 구현하면

장점 : 쉽게 구현 가능, setup 안 써도 됨

단점 : 매 페이지마다 getPageData 정의 필요, 추후 vue3로 마이그레이션시 하나하나 찾아서 수정해야함

 


+) 추가

계속 PageLoader 를 import 하는게 귀찮았고.. 하다보니 에러처리도 해야겠어서

기본 에러치리와 기본 로더를 추가했다.

const Suspense = Vue.extend({
  name: 'Suspense',
  data() {
    return {
      isPageLoading: false,
      isPageError: false,
    };
  },
  async created() {
    this.isPageLoading = true;
    const getPageData = (this.$parent as any).getPageData;
    const onPageError = (this.$parent as any).onPageError;

    if (!getPageData) {
      throw new Error('getPageData methods 가 존재하지 않습니다');
    }

    try {
      await getPageData();
    } catch (e) {
      this.isPageError = true;
      onPageError?.(e);
      console.error(e);
    } finally {
      this.isPageLoading = false;
    }
  },
  render(createElement) {
    if (this.isPageError) {
      return this.$slots.error ? createElement('div', this.$slots.error) : createElement(PageError);
    }

    if (this.isPageLoading) {
      return this.$slots.loader
        ? createElement('div', this.$slots.fallback)
        : createElement(PageLoader);
    }

    return createElement('div', this.$slots.default);
  },
});

 

결과!!

내가 대충 만들었더니 못생기긴 했다ㅜ

반응형