// ../router/index.js
let $state = { routes: [], parent: null };
export const router = () => {
const setRouterState = ({ routes, parent }) => {
$state = { routes, parent };
};
const useRouter = () => {
const { routes, parent } = $state;
if (!parent) {
document.body.innerHTML = "Error!";
return;
}
/** { result: location.pathName is route-path } */
const potentialMatches = routes.map((route) => {
return {
route,
result: location.pathname === route.path,
};
});
/** location.pathname = route-path */
const match = potentialMatches.find(
(potentialMatch) => potentialMatch.result !== false
);
if (!match) {
parent.innerHTML = "Error!";
return;
}
// match에 해당하는 route의 event를 실행
match.route.view.useEvent("view");
};
const navigateTo = (url) => {
history.pushState(null, null, url);
useRouter();
};
return { useRouter, setRouterState, navigateTo };
};
영상에서는 router 함수에서 직접 routes 들을 설정해서 관리를 했는데, 직접 router 파일에서 관리를 하는 것보다 Component들을 관리하는 app.js 에서 router,
routes 등을 설정해줘서 좀더 관리에 용이하도록 수정 하였다.
const potentialMatches = routes.map((route) => {
return {
route,
result: location.pathname === route.path,
};
});
potentialMatches 는 설정된 routes 중에서 현재 pathname 과 동일한 path 값을 갖으면 result 값을 true로 갖지 않으면 false로 반환하는 함수이다.
const match = potentialMatches.find(
(potentialMatch) => potentialMatch.result !== false
);
match 는 potentialMatches 에서 result 가 true 인 routes 를 의미한다.
match.route.view.useEvent("view");
나중에 Component 들이 공통으로 갖게될 useEvent 함수를 사용해서 현재 pathname과 동일한 path 값을 갖는 route의 component를 app.js 에서 사용 할 수 있도록 한다.
export class Application {
constructor({ target }) {
this.$target = target;
this.$state = {};
this.$routes = {};
this.$components = {};
this.$subscribe = [];
}
bindEvents() {}
init() {}
// 전체를 관리하는 Applictaion 에서 components를 관리 할 수 있도록 Set
setComponents({ name, component }) {
if (!name || !component) return;
this.$components[name] = component;
this.$components[name].setEvent("subscribe", ({ props, component }) => {
props.forEach((state) => {
this.$subscribe.push({ name: state, component: component });
});
});
this.$components[name].setSubscribe();
}
// 전체를 관리하는 Applictaion 에서 routes 관리 할 수 있도록 Set
setRoutes({ path, component }) {
this.$routes[path] = component;
this.$routes[path].setEvent("subscribe", ({ props, component }) => {
props.forEach((state) => {
this.$subscribe.push({ name: state, component: component });
});
});
this.$routes[path].setSubscribe();
}
setState(state) {
this.$state = { ...this.$state, ...state };
this.setProps();
}
// 어떤 상태가 변경되었을 때, 그 상태를 구독하고 있는 component 에 변경된 상태를 다시 전파한다.
setProps() {
this.$subscribe.forEach(({ name, component }) => {
if (!this.$state[name]) return;
let obj = {};
obj[name] = this.$state[name];
// component의 setProps는 component가 갖고 있는 props를 변경하는 것 (setProps와 유사)
component.setProps({ ...obj });
});
}
}
모든 Component 들을 관리하는 Class가 상속하는 함수이다.
// 전체를 관리하는 Applictaion 에서 components를 관리 할 수 있도록 Set
setComponents({ name, component }) {
if (!name || !component) return;
this.$components[name] = component;
// subscribe 이벤트를 설정 `$subscirbe` 에 각 $state 값을 구독하는 component를 넣어준다.
this.$components[name].setEvent("subscribe", ({ props, component }) => {
props.forEach((state) => {
this.$subscribe.push({ name: state, component: component });
});
});
// component에 설정된 "subscribe" 이벤트를 사용하는 함수
this.$components[name].setSubscribe();
}
setComponents 는 컴포넌트들을 담고 있는 변수 $components에 { 컴포넌트의 이름: 컴포넌트 } 의 형식의 object 를 넣어주는 함수 이다.
Component에서 Props 를 받게 되면 useEvent("subscribe", ({props, component})) 를 실행하는데, 동시에 Application의 subscribe에 각 App 이 갖고있는 state를 구독 하고 있는 Component를 담게 된다.
// 전체를 관리하는 Applictaion 에서 routes 관리 할 수 있도록 Set
setRoutes({ path, component }) {
this.$routes[path] = component;
this.$routes[path].setEvent("subscribe", ({ props, component }) => {
props.forEach((state) => {
this.$subscribe.push({ name: state, component: component });
});
});
this.$routes[path].setSubscribe();
}
setComponents 와 유사한 역할을 한다. route들을 담고 있는 변수 $routes에 { routes의 path: 컴포넌트 } 의 형식의 object 를 넣어주는 함수 이다.
$routes 는 나중에 setRouterState() 에 routes 값으로 설정된다.
setState(state) {
this.$state = { ...this.$state, ...state };
this.setProps();
}
$state의 값을 새로운 state 값을 갱신한다. 갱신된 state 를 구독하는 Component 들에 전파하는 setProps 함수를 실행한다.
setProps() {
this.$subscribe.forEach(({ name, component }) => {
if (!this.$state[name]) return;
let obj = {};
obj[name] = this.$state[name];
// component의 setProps는 component가 갖고 있는 props를 변경하는 것 (setProps와 유사)
component.setProps({ ...obj });
});
}
구독하고 있는 Component 들의 props들을 갱신시킨다.
export class Component {
constructor({ parent, props, initialState }) {
this.$parent = parent;
this.$state = initialState;
this.$props = props;
}
init() {}
render() {}
// 하위 컴포넌트에서 해당하는 event를 사용 (useEvent)하면 callback 함수를 실행 시킬 수 있게 한다.
setEvent(eventName, callback) {
this.events = this.events ? this.events : {};
this.events[eventName] = callback;
}
// eventName에 해당하는 함수 (setEvent에 등록한 event 의 Callback Function)실행
useEvent(eventName, payload) {
this.events[eventName] && this.events[eventName](payload);
}
// state 값을 갱신한다.
setState(state) {
this.$state = { ...this.$state, ...state };
this.render();
}
// props 들을 갱신한다. (상위 컴포넌트에서 실행)
setProps(props) {
this.$props = { ...this.$props, ...props };
this.render();
}
// 상위 컴포넌트에서 props로 들어온 값들을 상위 컴포넌트에 알리는 event
setSubscribe() {
if (this.$props) {
this.useEvent("subscribe", {
props: Object.keys(this.$props),
component: this,
});
}
}
}
출처(Router 설정): https://www.youtube.com/watch?v=6BozpmSjk-Y (Vanlia JS SPA)
출처(Component 설정): https://github.com/paullabkorea/theJungleFinalCodingTestFrontEnd