Core Improvements in Vue 3
- Performance Enhancements: Virtual DOM rewritten with static node skipping and dynamic node-only processing. Update performence improved by 1.3-2x, and SSR speed increased 2-3x.
- Tree-shaking Support: Removes unused modules during build. Based on static analysis of ES6 imports to identify and eliminate unused code.
- Fragment Support: Components can now have multiple root nodes, reducing unnecessary DOM nesting.
- Teleport: Allows rendering parts of a compnoent's template outside its DOM hierarchy.
- Suspense: Handles asynchronous dependencies in component trees, waiting for async components to resolve before rendering.
- TypeScript Integration: Better native support for TypeScript in the core library and APIs.
- Composition API: Introduces a more flexible API for organizing logic compared to the Options API.
- Proxy-based Reactivity: Replaces Object.defineProperty with ES6 Proxy for better reactivity tarcking.
Development Differences
1. Setup Function
The <script setup> syntax provides a more concise way to use the Composition API in single-file components.
import { defineComponent } from 'vue'
export default defineComponent({
props: {
title: String
},
setup(props) {
console.log(props.title)
}
})
2. Lifecycle Hooks
Lifecycle hooks are imported from 'vue' and called directly:
import { onMounted, onUpdated } from 'vue'
onMounted(() => {
// Component mounted
})
onUpdated(() => {
// Component updated
})
| Vue 2 | Vue 3 |
|---|---|
| beforeCreate | setup() |
| created | setup() |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeDestroy | onBeforeUnmount |
| destroyed | onUnmounted |
| errorCaptured | onErrorCaptured |
3. Reactive Data
- ref: Creates a reactive reference with a .value property
- reactive: Creates a deep reactive proxy of an object
import { ref, reactive } from 'vue'
export default {
setup() {
const count = ref(0)
const state = reactive({
num: 0,
list: [1, 2]
})
return { count, state }
}
}
4. Computed Properties
Computed values return a read-only ref unless a setter is provided:
import { computed } from 'vue'
const count = ref(1)
const double = computed(() => count.value * 2)
// With setter
const countWithSetter = ref(1)
const plusOne = computed({
get: () => countWithSetter.value + 1,
set: val => {
countWithSetter.value = val - 1
}
})
5. Watchers
- watchEffect: Runs immediately and tracks dependencies automatically
- watch: Watches specific sources and runs callbacks
import { watch, watchEffect } from 'vue'
watchEffect(() => {
console.log('Effect triggered')
})
watch(
() => state.count,
(newVal, oldVal) => {
console.log('Count changed:', newVal)
}
)
6. Template Refs
Refs can be used directly in templates to access DOM elements:
<template>
<div ref="root"></div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
const root = ref(null)
onMounted(() => {
console.log(root.value)
})
return { root }
}
}
</script>
7. Router, Store, and App Initialization
Vue 3 uses a more modular approach:
// Vue 3
import { createApp } from 'vue'
import { createStore } from 'vuex'
import { createRouter } from 'vue-router'
const app = createApp(App)
const store = createStore({ /*...*/ })
const router = createRouter({ /*...*/ })
app.use(store)
app.use(router)
app.mount('#app')
// Vue 2
import Vue from 'vue'
import Vuex from 'vuex'
import VueRouter from 'vue-router'
Vue.use(Vuex)
Vue.use(VueRouter)
new Vue({
store,
router,
render: h => h(App)
}).$mount('#app')
8. Custom Directives
Directives are registered with the app instance and have updated hook names:
import { createApp } from 'vue'
const app = createApp(App)
app.directive('custom', {
beforeMount(el, binding) {
// Similar to bind in Vue 2
},
mounted(el, binding) {
// Similar to inserted in Vue 2
},
beforeUpdate(el, binding) {
// New in Vue 3
},
updated(el, binding) {
// Similar to componentUpdated in Vue 2
},
beforeUnmount(el, binding) {
// New in Vue 3
},
unmounted(el, binding) {
// Similar to unbind in Vue 2
}
})
9. Emitting Events
In composition API, emit is obtained through the setup context:
<script setup>
import { defineEmits, ref, watch } from 'vue'
const emit = defineEmits(['toggle'])
const isOpen = ref(false)
function toggle() {
isOpen.value = !isOpen.value
emit('toggle', isOpen.value)
}
</script>