💚 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>