일반적으로 spa 구조의 어플리케이션을 빌드할 때 js가 하나로 나오며 그 크기가 비대해지는 경험
을 해봤을 것이다.
이렇게 되면 브라우저가 처음 index.html을 불러오고 DOM을 그릴때 js 로딩을 기다리는 시간이 길어지므로 FCP 즉 첫 화면이 그려지는데까지 오랜시간이 걸린다. 이는 CSR SPA 구조의 문제점
이었다.
그렇다면 하나로 번들링되는 js를 쪼개서 빌드할 수 없을까?
라는 생각에서 나온 대안 중 하나가 바로 동적 불러오기이다.
동적 불러오기를 적용하게 되면 js를 쪼개서 빌드할 수 있게되고
이는 브라우저가 DOM을 그릴때 첫화면에 필요한 js만 로드해서 페인팅할 수 있다는 말로 FCP가 앞당겨지며 화면이 빨리 나타나 최종적으로 사용자 경험이 좋아진다.
간단히 적용할 수 있다.
React이든 Vue든 사용방법은
const ** = ()=> import('./**')
위와 같은 형식으로 불러오는 파일을 읽어오면 된다.
적용하면 번들러로 무엇을 쓰든 js가 스필릿되어 빌드될 것이다.
보통 가장 많이 사용하는 곳은 라우터이다.
페이지를 로드할때 js 크기를 절감하여 화면을 빨리 그리려는 생각으로
라우터에 적용하게되고 일부 큰 크기가 컴포넌트에도 적용할 경우도 있다.
리액트에서는 lazy
라는 함수를 사용해 비동기적으로 리액트 컴포넌트를 불러와 리턴해주어 사용하는 경우가 많다.
const HomePage = lazy(async () => await import('../pages/HomePage.tsx'));
const HomePage = lazy(async () => await import('../pages/HomePage.tsx'));
const router = createBrowserRouter([
{
path: '/',
element: <HomePage />,
},
]);
const SomeComponent = lazy(async () => await import('./SomeComponent.tsx'))
function Example(){
...
return (
<>
<SomeComponent/>
</>
)
}
default export Example
뷰에서는 라우터에서 사용할때는 따로 전용함수를 사용하지 않아도 되지만
일반적인 vue 파일에서 사용할때는 defineAsyncComponent
함수를 사용해서 불러와야한다.
const HomePage = async () => await import('../pages/HomePage.vue');
const HomePage = async () => await import('../pages/HomePage.vue');
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
component: HomePage,
},
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
<template>
...
<SomeComponent/>
...
<template/>
<script setup lang='ts'>
const SomeComponent = defineAsyncComponent(() => import('./SomeComponent.vue'));
<script/>
페이지를 비동기적으로 불러올때 로딩시간이 길어진다면 흰화면을 보게 될 것으로 Suspense를 적용해 fallback UI를 적용하면 좋다.
React
<Suspense fallback={<div>loading...</div>}>
<RouterProvider router={router} />
</Suspense>
Vue
<Suspense>
<router-view/>
<template #fallback>
Loading...
</template>
</Suspense>
위처럼 적용하면 각각 loading UI를 적용해두면 느린 네트워크에도 화면이 비지 않고 나오기 좋다. 즉 사용자 경험이 좋아진다.
React는 ErrorBoundary
()를
Vue는 errorCaptured
, onErrorCaptured
를
사용해서 Error UI를 구현하고 적용하면 된다.
<ErrorBoundary>
<TestPage />
</ErrorBoundary>
여러 해결법이 있지만 에러 발생시 자동으로 새로고침하여 빌드된 저장소에서 새롭게 빌드된 파일을 받아오도록 하는 방법을 채택해서 적용했다.
리액트 라우터 사용예시
const retryLazy = (componentImport: any) =>
lazy(async () => {
const pageAlreadyRefreshed = JSON.parse(window.localStorage.getItem('pageRefreshed') ?? 'false');
try {
const component = await componentImport();
window.localStorage.setItem('pageRefreshed', 'false');
return component;
} catch (error) {
if (!pageAlreadyRefreshed) {
window.localStorage.setItem('pageRefreshed', 'true');
return window.location.reload();
}
throw error;
}
});
const HomePage = retryLazy(async () => await import('../pages/HomePage.tsx'));
const router = createBrowserRouter([
{
path: '/',
element: <HomePage />,
},
]);
Vue도 위와 유사하게 적용하면 된다.
https://velog.io/@goon126/%EC%B2%AD%ED%81%AC-%EC%97%90%EB%9F%AC