[Vue] Plain HTML에서 컴파일 없이 vue3 와 vue 컴포넌트를 사용하기

작성: 2023-11-01 12:25:32
수정: 2023-11-01 14:39:13
오구사십오

동기

현재 업무용 프로젝트에서 vue.js  기반의 SPA를 주력으로 개발하고 있는데, 부수적으로 서버단에서 인증 후 렌더링이 필요한 특정 페이지를 구현할 일이 생겼다. 일단은 급한대로 서버에 설치되어 있던 php와 Web Component를 이용하여 서버단 처리와 UI를 구현해 보았는데, 기존 메인 프로젝트에서 직접 구현한 UI컴포넌트를 직접 사용하고자 하는 욕심이 생겨 Plain HTML 파일에서도 컴파일 없이 vue 코드를 작성하고, vue 컴포넌트를 불러와 사용할 수 있는 방법을 찾아보게 되었다.

준비물

  1. 웹브라우저에서 직접 import 할 vue 라이브러리
  2. vue3-sfc-loader

구현 방법

다음과 같이 필요한 라이브러리를 import하고 createApp() 을 호출하여 원하는 요소를 vue.js application으로 만들면 된다.

<!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>vue3-sfc-loader 예제</title>
    <!-- vue3 라이브러리 및 sfc-loader import -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue3-sfc-loader/dist/vue3-sfc-loader.js"></script>
</head>
<body>
    <!-- #app 영역이 App.vue의 template영역이 됩니다. -->
    <div id="app">
      {{ message }}<br />
      <my-component></my-component>
    </div>

    <script>
    const { loadModule } = window['vue3-sfc-loader'];

    const options = {
      moduleCache: {
        vue: Vue
      },
      async getFile(url) {
        const res = await fetch(url);
        if ( !res.ok )
          throw Object.assign(new Error(res.statusText + ' ' + url), { res });
        return {
          getContentData: asBinary => asBinary ? res.arrayBuffer() : res.text(),
        }
      },
      addStyle(textContent) {
        const style = Object.assign(document.createElement('style'), { textContent });
        const ref = document.head.getElementsByTagName('style')[0] || null;
        document.head.insertBefore(style, ref);
      },
    }

    // App.vue 역할의 영역
    Vue.createApp({
      components: {
        'my-component': Vue.defineAsyncComponent( () => loadModule('../components/MyComponent.vue', options) )
      },
      data() {
        return {
          message: 'Hellow World',
        }
      }
    }).mount('#app');
    </script>
</body>
</html>

참고자료

유익했다면 후원해 주세요