on
Vanilla Javascript 라우터 구현
Vanilla Javascript 라우터 구현
1. 컨셉
하나의 라우터 엘리먼트를 생성한 뒤 라우터 엘리먼트의 자식노드로 정해둔 컴포넌트를 추가한다.
경로이동시 히스토리 변수에 해당 경로를 추가하여, 추후 이전 경로에 사용한다.
2. 구현
(1) webpack.config.js
devServer: { contentBase: path.join(__dirname, "public"), historyApiFallback: true, port: 8080, },
기존의 webapck.config에서 historyApiFallBack을 추가하여 history기능이 가능하도록 하였습니다.
(2) utils/selecter.ts
// 엘리먼트 검색함수 export function $(key: string): Element | boolean { if (!key) { return false; } return document.querySelector(key); } // 초기 template함수에 있는 엘리먼트들에 컴포넌트를 삽입하는 함수 // 선언시 삽입을 진행하고, 호출시 컴포넌트 라이프사이클이 실행됩니다. export function template(type, element, component) { const dom = document.createElement(type); return () => { element.appendChild(dom); new component(dom); }; } // 모든 자식노드 삭제함수 export function removeAllChild(parent: any) { while (parent.hasChildNodes()) { parent.removeChild(parent.firstChild); } }
(2) router/index.ts
let history = []; // 이전경로 호출함수 export const getHistory = () => { if (history.length === 1) { return false; } history = history.slice(0, -1); return history[history.length - 1]; }; // 이전경로로 이동후 해당경로가 히스토리에 다시 쌓이기 때문에 // 같은 경로만 계속 바라보게 되는 문제를 해결하기 위해 // 현재경로를 pop시키는 함수입니다. export const syncHistory = () => { history.pop(); }; // 이전경로 추가함수 export const addHistory = value => { history.push(value); };
(3) components/Header.ts
import Component from "../core/Component"; import {getHistory, syncHistory} from "../router"; export default class Header extends Component { template() { return ` Item Count 이전 `; } setEvent() { const {routing} = this.props; //App에 있는 routing함수 바인딩 this.addEvent("click", ".item", () => { routing("/item"); }); this.addEvent("click", ".count", () => { routing("/count"); }); this.addEvent("click", ".back", () => { const path = getHistory(); if (path) routing(path); syncHistory(); }); } }
props로 App에 구현되어 있는 routing함수를 바인딩 시킨값을 넘겨주었습니다.
(4) App.ts
import Component from "./core/Component"; import Item from "./components/Item"; import ItemAppender from "./components/ItemAppender"; import ItemFilter from "./components/ItemFilter"; import Count from "./components/Count"; import Header from "./components/Header"; import {$, template, removeAllChild} from "./utils/selecter"; import {addHistory} from "./router"; export default class App extends Component { Item; Count; Header; ItemFilter; ItemAppender; template() { return ` `; } routing(path) { const parent = $(".router"); removeAllChild(parent); switch (path) { case "/item": { this.ItemAppender(); this.Item(); this.ItemFilter(); break; } case "/count": { this.Count(); break; } default: { this.ItemAppender(); this.Item(); this.ItemFilter(); break; } } addHistory(path); } setEvent() {} mounted() { const {pathname} = window.location; const header = $(".header"); const router = $(".router"); new Header(header, {routing: this.routing.bind(this)}); this.Item = template("main", router, Item); this.ItemFilter = template("footer", router, ItemFilter); this.ItemAppender = template("header", router, ItemAppender); this.Count = template("div", router, Count); this.routing(pathname); } }
기존에 template에 엘리먼트 그대로 적혀있던 코드에서
router 앨리먼트에 자식으로 추가하는 방식으로 변경되었습니다.
기존에는 바로 인스턴스를 생성하여 라이프사이클이 진행되었지만,
라우팅을 하기 위해 선언만 하는 방식으로 변경하였습니다.
routing함수는 인스턴스를 생성하여 라이프사이클을 진행시킵니다.
라우팅시 각 경로는 addHistory에 의해서 History에 추가되어 이전 경로에 사용됩니다.
3. 전체코드
https://github.com/yg1110/Component/tree/router
from http://yg1110.tistory.com/36 by ccl(A) rewrite - 2021-09-27 05:27:11