10 Real‑World Vue 3 Migration Scenarios Every Developer Should Master
This article walks through ten common Vue 3 migration scenarios—parent‑child data flow, two‑way binding, router navigation, context access, slots, component caching, logic reuse, lifecycle hooks, global APIs, and TypeScript integration—providing clear explanations and complete code examples for each case.
Scenario 1: Parent‑Child Data Transfer
In Vue 3 the parent still passes props via attributes, but the child declares them with defineProps() inside <script setup>. The example shows a parent component importing ChildView.vue and passing some-prop="parent message", while the child uses defineProps({ someProp: { type: String, required: true } }) and logs the value.
Note: the four macros defineProps, defineEmits, defineExpose and withDefaults can only be used inside <script setup> and are compiled automatically.
Child‑to‑Parent Emission
Vue 2 uses $emit, which is unavailable in <script setup>. Instead, define an emit macro with defineEmits(['someEvent']) and call emit('someEvent', 'child message'). The parent receives the event via @some-event="someEvent".
Parent Access to Child Methods/Properties
Properties and methods are private by default. Expose them with defineExpose({ msg, change }) in the child, then reference the child via a ref in the parent and call child.value.msg or child.value.change() inside onMounted.
Scenario 2: Two‑Way Binding
Vue 2 used v-model or .sync. Vue 3 unifies the syntax to v-model with optional argument names (e.g., v-model:foo, v-model:bar). Internally this maps to :model-value="someValue" and @update:model-value="someValue = $event". The article provides a parent component using v-model="msg" on the child and the child defining defineProps(['modelValue']) and defineEmits(['update:modelValue']) to emit updates.
For input elements, you can bind directly with
:value="modelValue" @input="emit('update:modelValue', $event.target.value)"or use a computed wrapper that gets/sets modelValue and bind it with v-model="newValue".
Scenario 3: Router Navigation and Parameter Retrieval
In Vue 3 <script setup>, this.$router and this.$route are unavailable. Use the composition API: import useRouter and call
router.push({ path: '/about', query: { msg: 'hello vue3!' } }). To read parameters, import useRoute and access route.query.msg.
Scenario 4: Accessing the Component Context
The this object is not accessible in <script setup>. Retrieve the instance with getCurrentInstance(), then use const { ctx } = getCurrentInstance() to access the same properties as this. You can also use $parent and $refs from the context.
Scenario 5: Slot Usage
Vue 2 used the slot attribute and slot‑scope to pass scoped data. Vue 3 replaces this with the v-slot directive. The parent provides a template with v-slot:content="{ msg }", and the child defines a slot name="content" msg="'hello vue3!'". Vue 2.6+ also supports v-slot.
Scenario 6: Caching Route Components
Both Vue 2 and Vue 3 use KeepAlive to cache dynamic components. In Vue 3 you must wrap the routed component inside a RouterView slot:
<RouterView v-slot="{ Component }"><KeepAlive><Component :is="Component"/></KeepAlive></RouterView>. Lifecycle hooks onActivated and onDeactivated can run logic when a component is cached or restored.
Scenario 7: Logic Reuse
Vue 2 relied on mixins, which can cause naming conflicts. Vue 3 encourages the Composition API. The article shows a useMouse composable that tracks mouse coordinates with ref, registers mousemove listeners in onMounted, and cleans up in onUnmounted. It returns { x, y } for use in any component.
Another composable useRequest abstracts async data fetching: it creates data and error refs, performs axios.get(url), and returns the refs. Components can then render data, error, or a loading state.
Scenario 8: Lifecycle Changes
All Vue 3 lifecycle hooks start with on (e.g., onMounted) and must be imported. beforeCreate and created are removed; synchronous code placed directly in <script setup> runs before component creation.
Destruction hooks are renamed: beforeDestroy → onBeforeUnmount, destroyed → onUnmounted.
Scenario 9: Global API
In Vue 2 global properties were added to Vue.prototype. In Vue 3 you attach them to the app instance via app.config.globalProperties.$axios = axios. Components access it through
const { proxy } = getCurrentInstance(); proxy.$axios.get('...'). Global directives and components are also registered on the app instance (e.g., app.directive('focus', { mounted(el) { el.focus() } }) and app.component('CustomComp', CustomComp)). Filters are deprecated; use computed instead.
Scenario 10: Using TypeScript
Add lang="ts" to <script setup>. Props can be typed either by runtime declaration with
defineProps({ foo: { type: String, required: true }, bar: Number })or by generic type defineProps<Props>(). Default values for generic props are supplied with withDefaults.
Refs can be typed automatically, via the Ref interface, or by passing a generic type to ref<T>(initialValue). Reactive objects infer types from their initial shape, but you can also declare an interface and cast the result: const book: Book = reactive({ title: 'Vue 3 Guide' }).
The article concludes that mastering these Vue 3 APIs and the surrounding ecosystem (Pinia, Vite, etc.) will enable smooth migration from Vue 2.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
