Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
How to migrate from Vue 2 to Vue 3
// package.json
{
// ...
"dependencies": {
"vue": "^3.2.0", // UPDATE
"@vue/compat": "^3.2.0" // ADD
// ...
},
"devDependencies": {
"@vue/compiler-sfc": "^3.2.0" // ADD
"vue-template-compiler": "^2.6.0" // REMOVE
// ...
}
}
// webpack.config.js
module.exports = {
// ...
resolve: {
alias: {
vue: '@vue/compat'
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
compilerOptions: {
compatConfig: {
// Default everything to Vue 2 behavior
MODE: 2
}
}
}
}
]
}
}
<template>
<CustomDialog>
<template slot="heading" slot-scope="slotProps">
<h1>Items ({{ slotProps.items.length }})</h1>
</template>
</CustomDialog>
</template>
<template>
<CustomDialog>
<template #heading="slotProps">
<h1>Items ({{ slotProps.items.length }})</h1>
</template>
</CustomDialog>
</template>
<template functional>
<h1>{{ title }}</h1>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true,
},
}
}
</script>
<template>
<h1>{{ title }}</h1>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true,
},
}
}
</script>
<template>
<div
v-if="list"
v-for="item in list"
:key="item.id"
:title="item.title">
{{ item.title }}
</div>
</template>
<template>
<template v-if="list">
<div
v-for="item in list"
:key="item.id"
:title="item.title">
{{ item.title }}
</div>
</template>
</template>
<template>
<ul>
<li v-for="item in list">
<p v-if="item.amount < 10" :key="item.id">{{ item.title }}</p>
<p v-else class="high" :key="item.id">{{ item.title }}</p>
</li>
</ul>
</template>
<template>
<ul>
<li v-for="item in list">
<p v-if="item.amount < 10">{{ item.title }}</p>
<p v-else class="high">{{ item.title }}</p>
</li>
</ul>
</template>
<template>
<template v-for="item in list">
<div :key="item.id">{{ item.title }}</div>
</template>
</template>
<template>
<template v-for="item in list" :key="item.id">
<div>{{ item.title }}</div>
</template>
</template>
<style lang="scss">
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
</style>
<style lang="scss">
.v-enter-from,
.v-leave-to {
opacity: 0;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
</style>
import Vue from 'vue';
import App from 'views/app/app.vue'
new Vue({
router,
render: h => h(App)
}).$mount('#app');
import { createApp } from 'vue';
import App from 'views/app/app.vue'
const app = createApp(App);
app.mount('#app');
import somePlugin from 'plugins/some-plugin'
import someDirective from 'directives/some-directive'
Vue.use(somePlugin);
Vue.directive('some-directive', someDirective)
import somePlugin from 'plugins/some-plugin'
import someDirective from 'directives/some-directive'
app.use(somePlugin);
app.directive('some-directive', someDirective)
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
//
})
</script>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
//
})
</script>
Vue.prototype.$title = 'Some title'
app.config.globalProperties.$title = 'Some title'
// set
Vue.set(state.moduleName, 'keyName', 'value')
vm.$set(state.moduleName, 'keyName', 'value')
// delete
Vue.delete(state.moduleName, 'keyName', 'value')
vm.$delete(state.moduleName, 'keyName', 'value')
// set
Object.assign(state.moduleName, { keyName: 'value' })
// delete
delete state.moduleName.keyName
<script lang="ts">
this.$nextTick(() => {
// something DOM-related
})
</script>
<script lang="ts">
import { nextTick } from 'vue'
nextTick(() => {
// something DOM-related
})
</script>
<script lang="ts">
export default {
beforeDestroy() {
console.log('Before component unmounts!')
},
destroyed() {
console.log('Component unmounted!')
},
}
</script>
<script lang="ts">
export default {
beforeUnmount() {
console.log('Before component unmounts!')
},
unmounted() {
console.log('Component unmounted!')
},
}
</script>
<script lang="ts">
export default {
emits: ['click'],
methods: {
onTitleClick(title) {
this.$emit('click', title)
}
}
}
</script>
<template>
<MyComponent @hook:mounted="foo">
</template>
<template>
<MyComponent @vnode-mounted="foo">
</template>
<template>
<CustomComponent v-model="title" />
</template>
<template>
<CustomComponent v-model:value="title" />
</template>
<script lang="ts">
export default {
methods: {
updateTitle() {
this.$emit('input')
}
}
}
</script>
<script lang="ts">
export default {
// Don't forget to list the event in the 'emits' option
emits: ['update:modelValue'],
methods: {
updateTitle() {
this.$emit('update:modelValue')
}
}
}
</script>
<template>
<ChildComponent v-model="active" :title.sync="pageTitle" />
</template>
<template>
<ChildComponent v-model="active" v-model:title="pageTitle" />
</template>
const MyDirective = {
created(el, binding, vnode, prevVnode) {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeUnmount() {},
unmounted() {}
}
bind(el, binding, vnode) {
const vm = vnode.context
}
mounted(el, binding, vnode) {
const vm = binding.instance
}
<template>
<p>{{ user.lastName | uppercase }}</p>
</template>
<template>
<p>{{ uppercasedLastName }}</p>
<template>
<script lang="ts">
export default {
computed: {
uppercasedLastName(): string {
return this.user.lastName.toUpperCase()
}
},
// or alternatively
// {{ uppercaseText(user.lastName) }}
methods: {
uppercaseText(text: string) {
return text.toUpperCase()
}
}
}
</script>
<template>
<button is="someComponent" />
</template>
<template>
<component is="someComponent" />
</template>
// e.g. in app.ts
import mitt from 'mitt'
const emitter = mitt()
app.config.globalProperties.$emitter = emitter
this.$on('clickAway', this.hidePopover)
this.$emitter.on('clickAway', this.hidePopover)
<template>
<SpecialButton v-on:click.native="foo" />
</template>
<template>
<SpecialButton v-on:click="foo" />
</template>
<template>
<CustomComponent>Hello World</CustomComponent>
</template>
<script lang="ts">
export default {
mounted() {
console.log(this.$children[0])
},
}
</script>
<template>
<CustomComponent ref="greeting">Hello World</CustomComponent>
</template>
<script lang="ts">
export default {
mounted() {
console.log(this.$refs.greeting)
},
}
</script>
<script lang="ts">
export default {
mounted() {
console.log(this.$listeners)
}
}
</script>
<script lang="ts">
export default {
mounted() {
console.log(this.$attrs.onClick)
console.log(this.$attrs.onMouseenter)
}
}
</script>
<script lang="ts">
export default {
watch: {
someList: {
deep: true,
handler(val, oldVal) {
console.log('list was updated')
},
},
}
}
</script>
const MyComponent = {
component: () => import('./MyComponent.vue'),
// ...
}
import { defineAsyncComponent } from 'vue'
const MyComponent = defineAsyncComponent({
loader: () => import('./MyComponent.vue'),
// ...
})
<template>
<p :alt="false">Hello</p>
</template>
<template>
<p :alt="null">Hello</p>
</template>
export default new Vuex.Store<IRootState>(storeOptions)
export default createStore(storeOptions)
import Router from 'vue-router'
const router = new Router({
// ...
})
import { createRouter } from 'vue-router'
const router = createRouter({
// ...
})
import { createRouter, createWebHistory } from 'vue-router'
// there is also createWebHashHistory and createMemoryHistory
createRouter({
history: createWebHistory(),
routes: [],
})
// vuex-shim.d.ts
import { ComponentCustomProperties } from 'vue'
import { Store } from 'vuex'
// Your own store state
interface IMyState {
count: number
}
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$store: Store<IMyState>
}
}
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
declare module '*.vue' {
import { defineComponent } from 'vue'
const component: ReturnType<typeof defineComponent>
export default component
}
declare module 'vue/types/vue' {
export interface Vue {
$http: typeof axios
}
}
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$http: typeof axios
}
}
<script lang="ts">
export default {
props: {
modelValue: String // previously was `value: String`
},
emits: ['update:modelValue'],
methods: {
changeTitle(title) {
this.$emit('update:modelValue', title)
}
}
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment