Vue 3.0은 더 작고, 더 빠르며, TypeScript 지원 강화, API 설계 일관성 향상, 자체 유지보수성 증대, 그리고 더 많은 저수준 기능 접근성을 목표로 설계되었습니다.
주요 변경 사항
생명주기(Lifecycle Hooks) 변경
Vue 2.x에서 Vue 3.0으로 전환되면서 생명주기 훅의 사용 방식이 변경되었습니다. 특히, setup 함수가 핵심으로 도입되었으며, 대부분의 생명주기 훅은 Composition API의 일부로 setup 함수 내에서 호출됩니다.
beforeCreate→setupcreated→setupbeforeMount→onBeforeMountmounted→onMountedbeforeUpdate→onBeforeUpdateupdated→onUpdatedbeforeDestroy→onBeforeUnmountdestroyed→onUnmountedactivated→onActivateddeactivated→onDeactivatederrorCaptured→onErrorCapturedrenderTracked→onRenderTrackedrenderTriggered→onRenderTriggered
import {
onMounted,
onUpdated,
onUnmounted
} from 'vue';
export default {
setup() {
onMounted(() => {
console.log('컴포넌트가 마운트되었습니다.');
});
onUpdated(() => {
console.log('컴포넌트가 업데이트되었습니다.');
});
onUnmounted(() => {
console.log('컴포넌트가 마운트 해제되었습니다.');
});
// ... 다른 생명주기 훅들
}
}
Proxy를 이용한 반응성 구현
Vue 2는 Object.defineProperty를 사용하여 데이터 속성을 감지했습니다. 이 방식은 속성별로 감지가 이루어져 배열 변경 감지에 제약이 있고, IE8 이하 버전과의 호환성 문제가 있었습니다. Vue 3는 ES6의 Proxy 객체를 사용하여 객체 전체를 감지하는 방식으로 변경되었습니다. Proxy는 객체에 대한 기본 연산을 가로챌 수 있어, 더 효율적이고 포괄적인 반응성 추적이 가능합니다. 이로 인해 Vue 3는 최신 브라우저에 더 최적화되었습니다.
const originalData = {
count: 0
};
const handler = {
set(target, key, value) {
console.log(`값 변경: ${key} -> ${value}`);
return Reflect.set(target, key, value);
},
get(target, key) {
console.log(`값 접근: ${key}`);
return target[key];
}
};
const reactiveData = new Proxy(originalData, handler);
reactiveData.count++; // 콘솔에 "값 변경: count -> 1" 출력
console.log(reactiveData.count); // 콘솔에 "값 접근: count", 이후 1 출력
Diff 알고리즘 최적화
Vue 3의 Diff 알고리즘은 Virtual DOM을 기반으로 하지만, 컴파일러와 런타임의 협력을 통해 크게 최적화되었습니다. 주요 최적화 기법은 다음과 같습니다:
- 정적 트리 분석:
v-if,v-for와 같이 동적으로 구조가 변경되지 않는 부분은 컴파일 시점에 미리 파악하여 불필요한 재귀 탐색을 줄입니다. - 정적 속성 상승: 렌더링 과정에서 변경되지 않는 정적 노드나 객체는 렌더링 함수 외부로 추출하여 메모리 사용량과 가비지 컬렉션 부담을 줄입니다.
- 패치 플래그(Patch Flags): 동적 바인딩이 있는 요소에 대해 업데이트 종류에 따른 최적화 플래그를 생성하여, 런타임에서 불필요한 속성 비교를 생략하고 빠른 경로를 사용합니다.
TypeScript 지원 강화
Vue 2는 JavaScript 기반으로 개발되어 TypeScript와의 통합이 완벽하지 않았습니다. Vue 3는 처음부터 TypeScript로 작성되었으며, Composition API와 함께 강력한 타입 추론 및 지원을 제공하여 대규모 프로젝트의 유지보수성과 코드 가독성을 크게 향상시킵니다.
번들 크기 감소
Vue 3는 많은 전역 API와 내부 헬퍼 함수를 ES 모듈로 분리하여 Tree Shaking을 효과적으로 활용할 수 있도록 설계되었습니다. 이로 인해 사용하지 않는 코드는 번들에서 제거되어, Vue 2에 비해 번들 크기가 절반 이하로 줄었습니다. 압축 후 약 10KB 수준입니다.
호환되지 않는 변경 사항
Vue 3로 전환 시 주의해야 할 주요 비호환 변경 사항은 다음과 같습니다:
- 전역 API:
app.use(),app.component()등 애플리케이션 인스턴스 기반으로 변경되었습니다. - 템플릿 지시자:
v-model의 사용법이 변경되었고,.sync와v-bind.sync가 제거되었습니다.key속성 사용법이 변경되었습니다. - 컴포넌트: 함수형 컴포넌트 작성이 간소화되었고,
functional옵션이 제거되었습니다. 비동기 컴포넌트는defineAsyncComponent를 사용해야 합니다. - 렌더링 함수 API:
$scopedSlots가 제거되고$slots로 통합되었습니다.$listeners가$attrs로 통합되었습니다. - API 제거:
keyCode지원,$on,$off,$once인스턴스 메서드, 필터,$children등이 제거되었습니다.
간단한 Vue 3 컴포넌트 예제
Composition API를 사용한 간단한 예제입니다.
<template>
<div>
<h1>{{ greeting }}</h1>
<p>현재 카운트: {{ count }}</p>
<p>카운트의 두 배: {{ doubleCount }}</p>
<button @click="increment">카운트 증가</button>
</div>
</template>
<script>
import { ref, computed, onMounted } from 'vue';
export default {
name: 'CounterComponent',
setup() {
// 반응형 데이터 (ref 사용)
const count = ref(0);
const greeting = ref('Vue 3 카운터');
// 계산된 속성
const doubleCount = computed(() => count.value * 2);
// 메서드
const increment = () => {
count.value++;
};
// 생명주기 훅
onMounted(() => {
console.log('CounterComponent가 마운트되었습니다.');
});
// setup에서 반환되는 값들이 템플릿에서 사용 가능
return {
count,
greeting,
doubleCount,
increment,
};
}
}
</script>
<style scoped>
div {
font-family: sans-serif;
text-align: center;
}
button {
margin-top: 10px;
padding: 8px 15px;
cursor: pointer;
}
</style>