카테고리 없음

바닐라 자바스크립트로 SPA 구현하기

JoyYellow 2022. 10. 21. 17:04
  1. 핵심은 html의 <div class="root"></div> 태그에 코드를 넣고 빼는 것입니다.
  2. 뒤로가기를 위해 기록을 남깁니다.

구현

  • vallina-spa-test와 같은 폴더를 생성합니다(이름은 마음대로 선택할 수 있습니다)
  • 아래와 같은 폴더구조를 만듭니다.

 

  • index.html에 아래의 코드를 입력합니다.
    script 태그에 type="module"을 꼭 넣어줍니다.
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div class="root"></div>
  <script type="module" src="./index.js"></script>
</body>
</html>

 

  • index.js에 아래와 같이 코드를 입력합니다.
import App from './app.js';

const config = {
  el: '#root'
}

new App({ el: '#root' }).setup()

 

  • app.js 파일을 만들고 아래와 같이 입력합니다.
import { About, Home } from './pages/index.js';
import { Router } from './utils/index.js';
export default class App {
  constructor(props) {
    this.props = props;
  }
  setup() {
    const { el } = this.props;
    const router = new Router({
      '/': Home,
      '/about': About
    });
    router.init(el);
  }
}

 

  • pages/about.js파일에 아래와 같이 입력합니다.
export default class About {
  constructor() {
  }
  render() {
    return '<a href="/">Home</a>'
  }
}

 

  • pages/home.js파일에 아래와 같이 입력합니다.
export default class Home {
  constructor() {    
  }
  render() {
    return `<a href="/about">About</a>`
  }
}

 

  • pages/index.js파일에 아래와 같이 입력합니다.
export { default as About } from './about.js'
export { default as Home } from './home.js'

 

  • utils/index.js에 아래와 같이 입력합니다.
export { default as Router } from './router.js';

 

  • utils/router.js에 아래와 같이 입력합니다.
export default class Router {
  constructor(routes) {
    if (!routes) {
      console.error('Can not initialize routes, need routes!');
    }
    this.routes = routes
  }
  init(rootElementId) {
    if (!rootElementId) {
      console.error('Can not initialize Route, not define rootElementId');
      return;
    }
    this.rootElementId = rootElementId;
    this.routing(window.location.pathname);

    window.addEventListener('click', (e) => {
      if (e.target.closest('a')) {
        e.preventDefault();
        this.routePush(e.target.closest('a').href);
      }
    })
    window.onpopstate = () => this.routing(window.location.pathname)
  }
  routePush(pathname) {
    window.history.pushState({}, null, pathname);
    this.routing(window.location.pathname)
  }
  routing(pathname) {
    let page = "";
    if (this.routes[pathname]) {
      const component = new this.routes[pathname]();
      page = component.render()
    }
    if (page) {
      this.render(page)
    }
  }
  render(page) {
    const rootElement = document.querySelector(this.rootElementId)
    rootElement.innerHTML = page
  }
}

 

여기에서 window.onpopstate와 window.history.pushState가 중요하다.