💚 Vue.js 치트시트

점진적으로 채택할 수 있는 사용자 인터페이스 프레임워크

기본 컴포넌트

단일 파일 컴포넌트
<template>
    <div>
        <h1>{{ title }}</h1>
        <p>{{ message }}</p>
        <button @click="increment">Count: {{ count }}</button>
    </div>
</template>

<script>
export default {
    name: 'MyComponent',
    data() {
        return {
            title: 'Hello Vue!',
            message: 'Welcome to Vue.js',
            count: 0
        };
    },
    methods: {
        increment() {
            this.count++;
        }
    }
};
</script>
Composition API
<template>
    <div>
        <h1>{{ title }}</h1>
        <p>{{ message }}</p>
        <button @click="increment">Count: {{ count }}</button>
    </div>
</template>

<script setup>
import { ref, reactive } from 'vue';

const count = ref(0);
const state = reactive({
    title: 'Hello Vue!',
    message: 'Welcome to Vue.js'
});

const increment = () => {
    count.value++;
};
</script>
템플릿 문법
<template>
    <div>
        <!-- 텍스트 보간 -->
        <p>{{ message }}</p>
        
        <!-- 속성 바인딩 -->
        <img :src="imageSrc" :alt="imageAlt">
        
        <!-- 이벤트 바인딩 -->
        <button @click="handleClick">Click me</button>
        
        <!-- 조건부 렌더링 -->
        <div v-if="show">This is shown</div>
        <div v-else>This is hidden</div>
        
        <!-- 리스트 렌더링 -->
        <ul>
            <li v-for="item in items" :key="item.id">
                {{ item.name }}
            </li>
        </ul>
    </div>
</template>
Props와 Emits
<template>
    <div>
        <h2>{{ title }}</h2>
        <button @click="notifyParent">Notify Parent</button>
    </div>
</template>

<script>
export default {
    props: {
        title: {
            type: String,
            required: true
        }
    },
    emits: ['notify'],
    methods: {
        notifyParent() {
            this.$emit('notify', 'Hello from child!');
        }
    }
};
</script>

반응성 시스템

ref와 reactive
<script setup>
import { ref, reactive, computed, watch } from 'vue';

// ref - 단일 값
const count = ref(0);
const name = ref('');

// reactive - 객체
const state = reactive({
    firstName: 'John',
    lastName: 'Doe'
});

// computed - 계산된 속성
const fullName = computed(() => {
    return `${state.firstName} ${state.lastName}`;
});

// watch - 감시자
watch(count, (newValue, oldValue) => {
    console.log(`Count changed from ${oldValue} to ${newValue}`);
});
</script>
라이프사이클
<script>
export default {
    data() {
        return {
            message: 'Hello Vue!'
        };
    },
    beforeCreate() {
        console.log('beforeCreate');
    },
    created() {
        console.log('created');
    },
    beforeMount() {
        console.log('beforeMount');
    },
    mounted() {
        console.log('mounted');
    },
    beforeUpdate() {
        console.log('beforeUpdate');
    },
    updated() {
        console.log('updated');
    },
    beforeUnmount() {
        console.log('beforeUnmount');
    },
    unmounted() {
        console.log('unmounted');
    }
};
</script>
Composition API 라이프사이클
<script setup>
import { onMounted, onUnmounted, onUpdated } from 'vue';

onMounted(() => {
    console.log('Component mounted');
});

onUpdated(() => {
    console.log('Component updated');
});

onUnmounted(() => {
    console.log('Component unmounted');
});
</script>
provide/inject
<script>
// 부모 컴포넌트
export default {
    provide() {
        return {
            theme: 'dark',
            user: {
                name: 'John',
                age: 30
            }
        };
    }
};
</script>

<!-- 자식 컴포넌트 -->
<script>
export default {
    inject: ['theme', 'user'],
    mounted() {
        console.log(this.theme); // 'dark'
        console.log(this.user.name); // 'John'
    }
};
</script>

고급 기능

슬롯
<template>
    <div class="card">
        <header>
            <slot name="header"></slot>
        </header>
        <main>
            <slot></slot>
        </main>
        <footer>
            <slot name="footer"></slot>
        </footer>
    </div>
</template>

<!-- 사용법 -->
<Card>
    <template #header>
        <h2>Card Title</h2>
    </template>
    
    <p>Card content goes here</p>
    
    <template #footer>
        <button>Action</button>
    </template>
</Card>
커스텀 디렉티브
<script>
export default {
    directives: {
        focus: {
            mounted(el) {
                el.focus();
            }
        },
        color: {
            mounted(el, binding) {
                el.style.color = binding.value;
            },
            updated(el, binding) {
                el.style.color = binding.value;
            }
        }
    }
};
</script>

<template>
    <input v-focus>
    <p v-color="'red'">This text is red</p>
</template>
플러그인
// plugin.js
import { createApp } from 'vue';

const MyPlugin = {
    install(app, options) {
        // 전역 속성 추가
        app.config.globalProperties.$myMethod = (value) => {
            console.log(value);
        };
        
        // 전역 컴포넌트 등록
        app.component('MyComponent', {
            template: '<div>My Component</div>'
        });
        
        // 전역 디렉티브 등록
        app.directive('highlight', {
            mounted(el, binding) {
                el.style.backgroundColor = binding.value;
            }
        });
    }
};

// main.js
import { createApp } from 'vue';
import App from './App.vue';
import MyPlugin from './plugin';

const app = createApp(App);
app.use(MyPlugin);
app.mount('#app');
상태 관리 (Pinia)
// stores/counter.js
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
    state: () => ({
        count: 0,
        name: 'Eduardo'
    }),
    
    getters: {
        doubleCount: (state) => state.count * 2,
        doubleCountPlusOne() {
            return this.doubleCount + 1;
        }
    },
    
    actions: {
        increment() {
            this.count++;
        },
        reset() {
            this.count = 0;
        }
    }
});

// 컴포넌트에서 사용
<script setup>
import { useCounterStore } from '@/stores/counter';

const counter = useCounterStore();
</script>

<template>
    <div>
        <p>{{ counter.count }}</p>
        <p>{{ counter.doubleCount }}</p>
        <button @click="counter.increment">Increment</button>
    </div>
</template>
더 많은 Vue.js 학습 자료

체계적인 학습을 위해 다음 자료들도 확인해보세요