Historical archive
Vue 3 Migration Notes and API Changes
A detailed set of notes on migrating from Vue 2 to Vue 3, including the Composition API, application instances, tree shaking, v-model, async components, slots, and directives.
These notes summarize major Vue 3 and ecosystem changes for developers migrating from Vue 2.
New Vue 3 capabilities
Composition API
Vue 2’s Options API can scatter the data and methods for one feature across several component options. In a large component, related logic can be difficult to keep together.
Vue 3’s Composition API makes it easier to extract and group the operations associated with a piece of state.
Teleport
Teleport renders content outside the current component hierarchy, making it useful for UI such as modals that should be mounted beside the main application root.
Fragments
Vue 3 components no longer require a single root element.
The emits component option
Declare component events explicitly. The option can also validate events when expressed as an object.
<template>
<div @click="$emit('click')">
<h3>Custom event</h3>
</div>
</template>
<script>
export default {
emits: ['click'],
}
</script>
Explicit declarations also help distinguish component events from native listeners and avoid accidental double handling.
Custom renderers
Vue 3 exposes APIs for implementing custom rendering logic beyond the normal DOM renderer.
Global APIs move to the application instance
Vue 2 exposed global APIs such as Vue.component(). Every root instance shared the same global configuration, which could pollute other tests and prevented multiple applications on one page from having independent global settings.
Vue 3 introduces explicit application instances:
| Vue 2 global API | Vue 3 application API |
|---|---|
Vue.config |
app.config |
Vue.config.productionTip |
Removed |
Vue.config.ignoredElements |
app.config.isCustomElement |
Vue.component |
app.component |
Vue.directive |
app.directive |
Vue.mixin |
app.mixin |
Vue.use |
app.use |
Vue.filter |
Removed |
Tree-shakeable global and internal APIs
Many Vue 2 global APIs were static properties on the Vue constructor. Even when an application did not use them, bundlers could not always remove that dead code.
Affected APIs include:
Vue.nextTickVue.observable, replaced byreactiveVue.versionVue.compile, available only in full buildsVue.set, available only in compatibility buildsVue.delete, available only in compatibility builds
Vue 3 uses module imports so bundlers can omit unused APIs.
A unified v-model API
The component model option and the .sync modifier were replaced by argument-based v-model conventions:
<div id="app">
<h3>{{ data }}</h3>
<comp v-model="data"></comp>
</div>
app.component('comp', {
template: `
<div @click="$emit('update:modelValue', 'new value')">
I am comp: {{ modelValue }}
</div>
`,
props: ['modelValue'],
})
Render-function changes
The h function is no longer passed automatically and must be imported. The props structure is flatter, and scoped slots are unified with ordinary slots.
Functional components
Vue 3 functional components are plain functions:
- Their Vue 2 performance advantage is much smaller, so stateful components are generally recommended.
- A functional component receives
propsandcontext. - Single-file component templates no longer use the
functionalattribute. - The
{ functional: true }option was removed.
Async components
Async components must be wrapped explicitly with defineAsyncComponent():
- The
componentoption becameloader. - The loader no longer receives
resolveandreject. - The loader must return a Promise.
data is always a function
The component data option should be a function that returns the component’s reactive state.
Custom-element allowlists
Vue 3 detects custom elements while compiling templates. Configure isCustomElement for tags that Vue should leave alone. With vue-loader, place it in compiler options:
rules: [
{
test: /\.vue$/,
use: 'vue-loader',
options: {
compilerOptions: {
isCustomElement: tag => tag === 'plastic-button',
},
},
},
]
Changes to is and slots
- The
isattribute is limited to thecomponentelement in normal templates. - In-DOM templates use
v-isfor the compatibility behavior. $scopedSlotswas removed; all slots are available through$slotsand exposed as functions.
Attribute coercion
Most application developers do not need to interact directly with the lower-level coercion changes. See the archived Vue 3 migration overview.
Directive lifecycle hooks
Custom directive hooks now align with component lifecycle names:
bind→beforeMountinserted→mountedbeforeUpdateis newupdatewas removed in favor ofupdatedcomponentUpdated→updatedbeforeUnmountis newunbind→unmounted
These notes describe the Vue 3 migration surface near its initial release. Consult the current Vue documentation before applying them to a new project.