The Practical Differences You Care About
Vue 2 → Vue 3 Migration
If you're moving a Vue 2 app, the migration build plus a few mechanical changes get you 80% of the way there.
What you'll learn
- Use the @vue/compat migration build
- Replace filters with methods or computeds
- Update v-model to modelValue
Migrating from Vue 2 isn’t a rewrite. Most components run unchanged. The biggest changes are the new reactivity system, v-model contract, and a few removed features.
Migration Build
@vue/compat is Vue 3 with Vue 2 behavior turned on. It logs warnings for every incompatibility so you can fix incrementally.
npm i vue@^3 @vue/compat // vite.config.ts
export default {
resolve: {
alias: { vue: '@vue/compat' },
},
} Flip features one at a time as your code is ready.
Filters Are Gone
Replace {{ price | currency }} with a method or computed.
<!-- before -->
<p>{{ price | currency }}</p>
<!-- after -->
<p>{{ formatCurrency(price) }}</p> v-model on Components
Vue 2 used value + input; Vue 3 uses modelValue + update:modelValue.
<!-- Vue 2 child -->
<script>
export default {
props: ['value'],
methods: { update(v) { this.$emit('input', v) } },
}
</script>
<!-- Vue 3 child -->
<script setup lang="ts">
defineProps<{ modelValue: string }>()
const emit = defineEmits<{ 'update:modelValue': [string] }>()
</script> Vue 3 supports multiple v-models too: v-model:title, v-model:body.
Other Changes
Vue.extend,Vue.component,Vue.directive→ useapp.component,app.directiveon the app instance.$on/$off/$onceremoved (event bus pattern — use a library like mitt).- Async components: use
defineAsyncComponent. - Functional components: just plain functions, no
functional: true. - Reactive arrays: index assignment now reacts naturally — no more
Vue.set.
Composition API in Vue 2.7
If you’re still on Vue 2, upgrade to 2.7 — it backports the Composition API and <script setup>. You can start writing modern code today and migrate to Vue 3 later.