Moving from Vue 2 to Vue 3 is less about new syntax and more about a new way of organising a component. Here are the changes that made the biggest difference for me.
Options API to Composition API
In Vue 2, a component was split by type: data in one place, methods in another, computed and watch somewhere else. One feature ended up scattered across the whole file.
// Vue 2
export default {
data() {
return { count: 0 };
},
computed: {
double() {
return this.count * 2;
},
},
methods: {
increment() {
this.count++;
},
},
};
The Composition API lets you group code by what it does instead.
<!-- Vue 3 -->
<script setup>
import { computed, ref } from "vue";
const count = ref(0);
const double = computed(() => count.value * 2);
const increment = () => count.value++;
</script>
No this, no export default, and everything declared in <script setup> is available in the template.
The lifecycle hooks changed
The hooks are now functions you import, and a few were renamed.
beforeCreateandcreatedbecome thesetupcode itselfmountedbecomesonMountedbeforeUpdateandupdatedbecomeonBeforeUpdateandonUpdatedbeforeDestroybecomesonBeforeUnmountdestroyedbecomesonUnmounted
import { onBeforeUnmount, onMounted } from "vue";
onMounted(() => window.addEventListener("resize", onResize));
onBeforeUnmount(() => window.removeEventListener("resize", onResize));
The setup and the cleanup now sit next to each other, which makes leaks much harder to miss.
Composables replace mixins
Mixins were Vue 2's way to share logic, but you could never tell where a property came from. In Vue 3, shared logic is just a function.
// useCounter.js
import { ref } from "vue";
export function useCounter() {
const count = ref(0);
const increment = () => count.value++;
return { count, increment };
}
const { count, increment } = useCounter();
Every value has a visible source, and the logic is easy to test on its own.
Smaller changes worth knowing
- Reactivity just works. Vue 3 uses proxies, so adding a new property to an object or setting an array index is reactive. No more
Vue.set. v-modelon components usesmodelValueandupdate:modelValueinstead ofvalueandinput, and a component can now have more than onev-model.- Multiple root elements are allowed, so no more wrapper
divjust to keep the template valid.
Worth it
The move takes some getting used to, but components end up shorter, logic is easier to share, and features read top to bottom instead of being scattered. I would not go back.