SPA를 만들기 위해선 router 구현이 필요하다.
vanillaJS로 browser router를 구현해보았다.
기본적으로 SPA의 동작원리는 다음과 같다.
key를 url path로 value를 component로 하는 객체 리터럴을 만든다.
페이지 이동을 할 때에 이 객체 리터럴에서 페이지에서 파싱한 값을 통해 component를 조회하여 렌더링한다
페이지 이동을 정의하는 방법엔 두 가지가 있다. 함수를 통해 pushState로 바꿔주고 innerHTML을 바꾸어주거나 혹은 이 작업을 커스텀 이벤트로 정의하는 방식이다.
두 번째( 커스텀 이벤트를 정의하는 방식 )으로 browser router를 구현해보았다.
Router 구조 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Router { constructor ( ) {} initEvent ( ) {} onRouterChangeHandler ( ) {} hasRoute ( ) {} getRoute ( ) {} renderPage ( ) {} push ( ) {} }
Router 초기화 1 2 3 4 5 6 7 8 9 10 11 12 13 constructor ({ $app,routes,fallback='/' } ) { this .$app=$app; this .fallback=fallback; this .routes=[]; routes.forEach(route => { this .routes=[route.path]=route.page; }) this .initEvent(); }
$app,fallback,route를 초기화하고 initEvent를 실행한다.
커스텀 이벤트 추가 browser router는 history API를 사용하여 주소를 변경한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 initEvent ( ) { document .addEventListener( "moveRoutes" , this .moveRoutesHandler.bind(this ) as EventListener ); } moveRoutesHandler (event: CustomEvent ) { const path: string = event.detail.path; history.pushState(event.detail, "" , path); this .renderPage(path); }
moveRoutes라는 이벤트를 추가해주어야 한다.
1 2 3 4 5 6 7 export const customEventEmitter = (eventType: string, detail?: object ) => { document .dispatchEvent( new CustomEvent(eventType, { detail, }) ); };
커스텀 이벤트를 정의하는 함수를 만든다.
페이지 렌더링 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 hasRoute (path: string ) { return typeof this .routes[path] !== "undefined" ; } getRoute (path: string ) { return this .routes[path]; } renderPage (path: string ) { let route; const regex = /\w{1,}$/ ; if (this .hasRoute(path)) { route = this .getRoute(path); } else if (regex.test(path)) { route = this .getRoute(path.replace(regex, ":id" )); } else { route = this .getRoute(this .fallback); } new route(this .$app, {}); } push (path: string ) { customEventEmitter("moveRoutes" , { ...history.state, path, }); }
라우터 export 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 export let router;export function initRouter ($app, routes, fallback ) { const routerObj = new Router($app, routes, fallback); router = { push : (path ) => routerObj.push(path), }; customEventEmitter( "moveRoutes" , history.state ?? { path : "/" , } ); }
모듈패턴을 이용하여 push를 제외한 나머지는 private로 두었다.
그리고 초기에 customEventEmitter를 통해 루트 페이지가 렌더링 될 수 있도록 하였다.
사용하기 1 2 3 4 5 6 7 const routes = [ { path : "/" , page : App }, { path : "/detail/:id" , page : Detail }, ]; const target = document .querySelector(".App" );initRouter(target, routes);
Ref 바닐라JS(TS)로 리액트 SPA 구현하기 | (4) 클래스로 BrowserRouter 구현 Build a custom SPA Router using VanillaJS