728x90
반응형
상태관리 (스토어)
상태 관리 : 데이터(상태)를 통신할 수 있는 관리 개념
상태 관리 패턴 : 상태를 관리
스토어 : 상태가 저장된 공간
CORE.JS
// 컴포넌트, 라우터, 스토어(컴포넌트와 컴포넌트의 데이터 통신 기능) 기능 포함
/* =========== Component ============ */
// tagName을 받거나, el 속성에 요소를 메모리 상에만 하나 추가하고,
// 사용되는 부분에서 정의되는 render 함수 부분을 실행
export class Component {
// payload : App.js에서 요소 생성에서 필요한 태그 인수인데, 객체 데이터를 payload에 저장
constructor(payload = {}) {
// 객체 구조 분해 할당
// payload 객체 데이터의 tagName 속성에서 데이터를 꺼냄
const {
tagName = 'div',
state = {},
props = {}
} = payload
// this가 붙어있는 경우, 클래스 내부에서 만들어지는 모든 메소드 안에서
// this 키워드로 참조해서 사용 가능
this.el = document.createElement(tagName)
this.state = state
this.props = props
this.render() // 최초 1번만 호출
}
// 컴포넌트 클래스를 App.js, 다른 js 파일에서 확장해서 사용할 때만 동작
render() {
}
}
/* =========== Router ============ */
// 페이지
function routeRender(routes) {
if(!location.hash){
// replaceState : 히스토리 내역에 기록을 남기지 않고, 페이지 이동
history.replaceState(null, '', '/#/')
}
const routerView = document.querySelector('router-view')
// http://localhost:1234/#/about?name=test
// hash : #/about?name=test
// ? 기준으로 쿼리 스트링과 hash 부분을 구분해야함
const [hash, qeuryString = ''] = location.hash.split('?')
// a=123&b=456
// ['a=123', 'b=456']
const query = qeuryString
.split('&')
.reduce((acc, cur)=>{
const [key, value] = cur.split('=')
acc[key] = value
return acc
}, {})
history.replaceState(query, '')
const currentRoute = routes.find(route => {
return new RegExp(`${route.path}/?$`).test(hash)
})
routerView.innerHTML = ''
routerView.append(new currentRoute.component().el)
// 모든 기능 실행 후, 페이지 최상단 이동
window.scrollTo(0,0)
}
export function createRouter(routes) {
return function () {
window.addEventListener('popstate', () => {
routeRender(routes)
})
routeRender(routes)
}
}
/* =========== Store ============ */
// 상태들이 저장되는 장소
// 데이터가 수정될 때마다 화면이 갱신되어야할 때 상태를 관리하는 상태관리패턴 사용
export class Store {
constructor(state) {
this.state = {}
this.observers = {}
for (const key in state) {
// 객체 데이터의 속성 정의
Object.defineProperty(this.state, key, {
get: () => {
return state[key] // state['message']
},
set: (val) => {
state[key] = val
this.observers[key]().forEach(observer => observer(val))
}
})
}
}
// state가 변경되는지 확인
subscribe(key, cb){
Array.isArray(this.observers[key])
? this.observers[key].push(cb)
: this.observers[key] = cb
}
}
HOME.JS
import { Component } from "../core/core.js";
import TextField from "../components/TextField.js";
import Message from "../components/Messgae.js"
import Title from "../components/Title.js"
export default class Home extends Component {
render() {
this.el.innerHTML = /*html*/ `
<h1>Home Page!</h1>
`
this.el.append(
new TextField().el,
new Message().el,
new Title().el
)
}
}
Title.js
import { Component } from "../core/core";
import messageStore from "../store/message";
export default class Title extends Component {
constructor() {
super({
tagName : 'h1'
})
messageStore.subscribe('message', (newVal) => {
console.log(newVal)
this.render()
})
}
render() {
this.el.textContent = `Title: ${messageStore.state.message}`
}
}
TextField.js
import { Component } from "../core/core.js";
import messageStore from '../store/message.js'
export default class TextField extends Component {
render() {
this.el.innerHTML = /*html*/ `
<input value="${messageStore.state.message}"/>
`
const inputEl = this.el.querySelector('input')
inputEl.addEventListener('input', () => {
messageStore.state.message = inputEl.value
})
}
}
Message.js (앞에 대문자는 모듈)
import { Component } from "../core/core.js";
import messageStore from '../store/message.js';
export default class Message extends Component {
constructor() {
super()
}
render() {
this.el.innerHTML = /*html*/`
<h2>${messageStore.state.message}</h2>
`
}
}
message.js
import { Store } from "../core/core";
export default new Store({
message: 'Hello~'
})
728x90
반응형
'JS, TS' 카테고리의 다른 글
[TS] 기본 문법 2 (1) | 2024.01.22 |
---|---|
[TS] 기본 문법 1 (1) | 2024.01.22 |
[JS] 컴포넌트 - 해시 라우터 관리 (0) | 2024.01.16 |
[JS] 컴포넌트 (0) | 2024.01.16 |
[JS] 정규표현식 (0) | 2024.01.16 |