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, elemnent = null) {
if (!key) {
return false;
}
if (!elemnent) {
return document.querySelector(key);
} else {
return elemnent.querySelector(key);
}
}
// 엘리먼트 삽입후 컴포넌트 생성함수
export function createComponent(type, element, name = null, component) {
const dom = document.createElement(type);
if (!!name) {
dom.classList.add(name);
}
element.appendChild(dom);
new component(dom);
}
// 모든 자식노드 삭제함수
export function removeAllChild(parent: any) {
while (parent.hasChildNodes()) {
parent.removeChild(parent.firstChild);
}
}
(2) router/index.ts
import {$, createComponent, removeAllChild} from "../utils/selector";
import ItemAppender from "../components/ItemAppender";
import Item from "../components/Item";
import ItemFilter from "../components/ItemFilter";
import Count from "../components/Count";
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);
};
export function subRoute(path) {
const {$el} = this;
removeAllChild($el);
switch (path) {
case "/item": {
createComponent("header", $el, "append", ItemAppender);
createComponent("main", $el, "item", Item);
createComponent("footer", $el, "filter", ItemFilter);
break;
}
case "/count": {
createComponent("div", $el, "count", Count);
break;
}
default: {
break;
}
}
addHistory(path);
}
subRoute함수가 실행될때 마다 모든 자식노드를 삭제후 시작합니다.
그 후 경로에 따라 앨리먼트를 삽입하고 그 앨리먼트 이용해서 컴포넌트를 생성합니다.
라우팅시 각 경로는 addHistory에 의해서 History에 추가되어 이전 경로에 사용됩니다.
(3) components/Header.ts
import Component from "../core/Component";
import {getHistory, syncHistory} from "../router";
export default class Header extends Component {
template() {
return `
<button class="item">Item</button>
<button class="count">Count</button>
<button class="back">이전</button>
`;
}
created() {
const {subRoute} = this.props;
subRoute("/item");
}
setEvent() {
const {subRoute} = this.props;
this.addEvent("click", ".item", () => {
subRoute("/item");
});
this.addEvent("click", ".count", () => {
subRoute("/count");
});
this.addEvent("click", ".back", () => {
const path = getHistory();
if (path) {
subRoute(path);
}
syncHistory();
});
}
}
created 라이프사이클을 추가하여 초기 실행시 item경로가 호출되게 하였으며,
각 메뉴 클릭시 해당경로로 라우팅시킵니다.
이전버튼 클릭시 이전경로를 불러와서 라우팅시키며, 위에서 설명 했던것과 같이 히스토리 경로를 sync시켜 줬습니다.
(4) Page/Root.ts
import Component from "../core/Component";
import Header from "../components/Header";
import {subRoute} from "../router";
import {$} from "../utils/selector";
export default class Root extends Component {
template() {
return `
<header class="header"></header>
<div class="sub-route"></div>
`;
}
mounted() {
const header = $(".header");
this.$el = $(".sub-route");
new Header(header, {
subRoute: subRoute.bind(this),
});
}
}
Root페이지를 따로 만들어서 URL에 따른 다른 페이지를 구현하였습니다.
subRoute함수는 bind를 걸어 this 호출 스코프를 변경 하였습니다.
(5) Page/NotFound.ts
import Component from "../core/Component";
export default class NotFound extends Component {
template() {
return `
<div>NotFound</div>
`;
}
}
지정하지 않은 URL로 접속하였을경우 나오는 페이지입니다.
(6) App.ts
import Component from "./core/Component";
import {$} from "./utils/selector";
import Root from "./page/Root";
import NotFound from "./page/NotFound";
export default class App extends Component {
template() {
return `
<div class="router-dom"></div>
`;
}
mounted() {
const {pathname} = window.location;
const router = $(".router-dom");
switch (pathname) {
case "/": {
new Root(router);
break;
}
default: {
new NotFound(router);
break;
}
}
}
}
각 URL에 따른 페이지 라우팅입니다.
3. 전체코드
https://github.com/yg1110/Component/tree/router
'Web' 카테고리의 다른 글
Scope (0) | 2022.03.19 |
---|---|
웹페이지 동작 원리 & 브라우저 랜더링 과정 (0) | 2022.03.18 |
Vanilla Javascript Component Core 구현 (0) | 2021.09.26 |
WebPack을 이용한 Typescript 번들링 (0) | 2021.08.09 |
Class (0) | 2020.05.01 |