참고 :

https://v3-docs.vuejs-korea.org/guide/quick-start.html#enabling-import-maps

https://v3-docs.vuejs-korea.org/examples/#simple-component

 

build방식이 아닌 간단하게 화면 구성시 활용할 수 있는  CDN방식 + build방식에서 사용하는 html template방식으로 구성하는 예시.

 

base.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <title>{% block title %}{{ site.name }}{% endblock title %}</title>
    <meta charset="UTF-8">
    <meta name="csrf_token" content="{{ csrf_token }}">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    {% load static %}
    <link rel="stylesheet" type="text/css" href="{% static "style/base.css" %}" />
    <link rel="stylesheet" type="text/css" href="{% block extraStyle %}{% endblock extraStyle %}" />
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" />

    <script type="text/javascript" src="{% static "javascript/UT.js" %}"></script>
    <script type="text/javascript" src="{% static "javascript/UI.js" %}"></script>
    <script type="text/javascript" src="{% static "javascript/SS.js" %}"></script>
    <script type="importmap">
        {
          "imports": {
            "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.prod.js"
          }
        }
    </script>
    <script src="{% static "javascript/VueUtil.js" %}" type="module"></script>
    <script src="{% block extraScript %}{% endblock extraScript %}" type="text/javascript"></script>
</head>

<body>
    <div id="header">
        {% include "header.html" %}
    </div>

    <div id="content">
        {% block content %}{% endblock content %}
    </div>
    
    <div id="footer">
        {% include "footer.html" %}
    </div>
</body>
</html>

 

home.html

{% extends "base.html" %}

{% block title %}{{ site.name }}{% endblock title %}

{% load static %}
{% block extraStyle %}{% endblock extraStyle %}
{% block extraScript %}{% endblock extraScript %}

{% block content %}

<div id="app">
    <button @click="count++">
      숫자 세기: {% verbatim %}{{ count }}{% endverbatim %}
    </button>
  
    <ol>
      <todo-item
        v-for="item in groceryList"
        :todo="item"
        :id="item.id"
      ></todo-item>
    </ol>
</div>
    
<script type="module">
import { createApp, ref } from "vue"

const app = createApp({
    setup() {
        const groceryList = ref([
            { id: 0, text: "Vegetables" },
            { id: 1, text: "Cheese" },
            { id: 2, text: "Whatever else humans are supposed to eat" }
        ])

        const count = ref(0)

        return {
            count,
            groceryList
        }
    },

    mounted() {
        console.log(this.groceryList)
    }
})
.component("todo-item", VueUtil.loadModule("/vue/TodoItem.vue"))
.mount("#app")

app.config.errorHandler = (err, instance, info) => {
  // 에러 핸들링: 서비스 에러 로그 기록
}
</script>

{% endblock content %}

 

TodoItem.vue

<template>
  <li @click="test(todo.text)">{{ todo.id }} : {{ todo.text }}</li>
</template>

<script>
export default {
    props: {
        todo: Object
    },
    
    setup(props, context) {
        console.log(props.todo)

        // 속성 (비-반응형 객체, $attrs에 해당함)
        console.log(context.attrs)

        // 슬롯 (비-반응형 객체, $slots에 해당함)
        console.log(context.slots)

        // 이벤트 발송 (함수, $emit에 해당함)
        console.log(context.emit)

        // 로컬 속성 노출 (함수)
        console.log(context.expose)
    },

    methods: {
        test(text) {
            console.log(text);
        }
    },

    mounted() {
        console.log("parkingLot_list.vue")
    }
}
</script>

<style scoped>
li {
  font-weight: bold;
  color: red;
}
</style>

 

VueUtil.js

import * as Vue from "vue"
import { loadModule } from "https://unpkg.com/vue3-sfc-loader/dist/vue3-sfc-loader.esm.js"

const VueUtil = new (function()
{
    function _vueUtil()
    {

    };

    _vueUtil.prototype.loadModule = function(url)
    {
        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);
            },
        }

        return Vue.defineAsyncComponent(() => loadModule(url, options));
    };

    return _vueUtil;
}());

window.VueUtil = VueUtil;

 

 

 

출력결과