SoFunction
Updated on 2025-04-14

Tutorials and guide to avoiding pits when upgrading Vue2 to Vue3

1. Why upgrade to Vue3?

Before going deeper into the upgrade details, let’s take a look at the main advantages of Vue3:

1. Significant performance improvement: Responsive systems implemented through Proxy are more efficient than those of Vue2, and virtual DOM rewrites reduce runtime overhead

2. Better TypeScript support: The Vue3 code base itself is written in TypeScript, providing better type inference

3. Combination API: Solves the code organization problem caused by increasing component complexity in Vue2

4. Smaller volume: Through Tree-shaking optimization, Vue3 core volume is about 40% smaller than Vue2

5. New features: Fragment, Teleport, Suspense and other new features provide more possibilities for development

2. API changes and compatibility processing

1. Lifecycle hook rename

In Vue3, most life cycle hooks have "on" prefixes and need to be used in setup():

// Vue2
export default {
  created() {},
  mounted() {},
  beforeDestroy() {}
}

// Vue3
import { onMounted, onBeforeUnmount } from 'vue'
export default {
  setup() {
    onMounted(() => {})
    onBeforeUnmount(() => {})
  }
}

Notice:

  • beforeCreateandcreatedquiltsetup()Alternative
  • beforeDestroyChange toonBeforeUnmount
  • destroyedChange toonUnmounted

2. Changes in v-model

V-model has been made major adjustments in Vue3:

<!-- Vue2 -->
<ChildComponent v-model="pageTitle" />

<!-- Equivalent to -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />

<!-- Vue3 -->
<ChildComponent v-model="pageTitle" />

<!-- Equivalent to -->
<ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event" />

Key changes:

  • defaultprop fromvalueChange tomodelValue
  • Default event frominputChange toupdate:modelValue
  • Multiple v-model bindings can be supported:v-model:title="pageTitle"

3. Event API changes

Vue3 removes the o n , on , on , off and $once methods. It is recommended to use external libraries such as mitt to implement event bus mode:

// Vue2
const bus = new Vue()
bus.$on('event', handler)
bus.$emit('event', params)

// Vue3
import mitt from 'mitt'
const emitter = mitt()
('event', handler)
('event', params)

4. Filter removal

Vue3 removes the filter function, and it is recommended to use method or computed attribute instead:

// Vue2
{{ message | capitalize }}

filters: {
  capitalize(value) {
    if (!value) return ''
    return ().charAt(0).toUpperCase() + (1)
  }
}

// Vue3
{{ capitalize(message) }}

methods: {
  capitalize(value) {
    if (!value) return ''
    return ().charAt(0).toUpperCase() + (1)
  }
}

3. Composition API (Composition API) best practices

Combination API is one of the most important new features of Vue3, which solves the code organization problem in Vue2 as component complexity increases.

1. Basic use

import { ref, computed, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const doubleCount = computed(() =>  * 2)
    
    function increment() {
      ++
    }
    
    onMounted(() => {
      ('Component mounted')
    })
    
    return {
      count,
      doubleCount,
      increment
    }
  }
}

2. Logical reuse

Combined API makes logical multiplexing easier and can be extracted into independent functions:

// 
import { ref, onMounted } from 'vue'
import axios from 'axios'

export default function useUser(userId) {
  const user = ref(null)
  const loading = ref(false)
  
  const fetchUser = async () => {
     = true
    try {
      const response = await (`/api/users/${userId}`)
       = 
    } finally {
       = false
    }
  }
  
  onMounted(fetchUser)
  
  return {
    user,
    loading,
    refetch: fetchUser
  }
}

// Use in componentsimport useUser from './useUser'

export default {
  props: ['userId'],
  setup(props) {
    const { user, loading, refetch } = useUser()
    
    return {
      user,
      loading,
      refetch
    }
  }
}

3. Mixed with the Options API

Vue3 fully supports the Options API, you can migrate step by step:

export default {
  // Options API
  data() {
    return {
      traditionalData: 'old way'
    }
  },
  
  // Composition API
  setup() {
    const modernData = ref('new way')
    
    return {
      modernData
    }
  },
  
  methods: {
    traditionalMethod() {
      ()
      ()
    }
  }
}

4. Responsive system rewriting

Vue3 uses Proxy instead of implementation responsiveness, which brings performance improvements but also introduces some changes.

1. Responsive API changes

// Vue2
export default {
  data() {
    return {
      obj: {
        a: 1
      },
      arr: [1, 2, 3]
    }
  },
  methods: {
    mutateData() {
       = 2 // Non-responsive      this.$set(, 'b', 2) // Responsive      [0] = 9 // Non-responsive      this.$set(, 0, 9) // Responsive    }
  }
}

// Vue3
import { reactive } from 'vue'

export default {
  setup() {
    const obj = reactive({ a: 1 })
    const arr = reactive([1, 2, 3])
    
    function mutateData() {
       = 2 // Responsive      arr[0] = 9 // Responsive    }
    
    return {
      obj,
      arr,
      mutateData
    }
  }
}

2. ref and reactive

Vue3 provides two ways to create responsive data:

import { ref, reactive } from 'vue'

// ref - suitable for basic typesconst count = ref(0)
() // Access value
// reactive - suitable for objectsconst state = reactive({
  count: 0
})
() // Access value

Best Practices:

  • Basic type usageref

  • Object usagereactive

  • In the template, ref will be automatically unpacked and does not need to.value

5. Template related changes

1. Fragments

Vue3 supports multiple node templates:

<!-- Vue2 - A single root element is required -->
<template>
  <div>
    <header></header>
    <main></main>
    <footer></footer>
  </div>
</template>

<!-- Vue3 - Support multiple elements -->
<template>
  <header></header>
  <main></main>
  <footer></footer>
</template>

2. Custom directive API changes

The life cycle hook of a custom directive is consistent with the component:

// Vue2
('focus', {
  bind(el, binding, vnode) {},
  inserted(el, binding, vnode) {},
  update(el, binding, vnode, oldVnode) {},
  componentUpdated(el, binding, vnode, oldVnode) {},
  unbind(el, binding, vnode) {}
})

// Vue3
('focus', {
  beforeMount(el, binding, vnode) {},
  mounted(el, binding, vnode) {},
  beforeUpdate(el, binding, vnode, prevVnode) {},
  updated(el, binding, vnode, prevVnode) {},
  beforeUnmount(el, binding, vnode) {},
  unmounted(el, binding, vnode) {}
})

3. Transitional class name changes

The class name of the transition animation has been adjusted:

v-enter → v-enter-from
v-leave → v-leave-from

6. Migration strategies and tools

1. Official migration build version

Vue3 provides a build version that is compatible with Vue2 behavior, which can help with step-by-step migration:

import Vue from 'vue/dist/-bundler' // Vue2 compatibility mode

2. Use migration assistance tools

The Vue team provides official migration tools:

1. Install the @vue/compat package

2. Gradually enable Vue3 features by configuring the compatibility switch

3. Use eslint-plugin-vue to detect incompatible code

3. Gradually migrate strategy

1. Evaluation phase:

  • Check if the third-party library used in the project has a Vue3 compatible version

  • Run the project using Vue2 compatible mode

2. Preparation phase:

  • Remove obsolete API usage (filter, $on, etc.)

  • Refactoring mixins into a combinatorial function

  • Make sure all life cycle hooks use new names

3. Migration phase:

  • Starting with simple components, gradually migrating

  • Using single file component mode

  • Prioritize business core components

4. Testing phase:

  • Comprehensive test feature regression

  • Performance benchmarking

  • Compatibility testing

7. Frequently Asked Questions and Solutions

1. Third-party library compatibility

Problem: Many Vue2 plugins do not support Vue3 yet

Solution:

  • Check if the plugin has Vue3 version

  • Find alternatives

  • Consider self-encapsulation compatibility layer

2. Performance optimization

Although Vue3 performs better, it still needs to be noted:

  • Avoid using complex expressions in templates

  • Use shallowRef and shallowReactive rationally to reduce unnecessary responsive overhead

  • Optimize static content with v-once

3. TypeScript integration

  • Vue3 supports TypeScript better:

  • Use defineComponent to define components to obtain type inference

  • Define clear types for props and emits

  • Advantages of Type Derivation with Combined APIs

import { defineComponent } from 'vue'

export default defineComponent({
  props: {
    message: {
      type: String,
      required: true
    }
  },
  setup(props) {
    // Props has correct type inference    ()
  }
})

8. Summary

The migration of Vue2 to Vue3 is a system project that requires a comprehensive understanding of API changes, responsive system differences and new features. With reasonable migration strategies and tool support, upgrade risks can be minimized. It is recommended to adopt a progressive migration solution, starting with the new components, gradually renovating the old components, and finally completing a comprehensive upgrade.

Upgrading to Vue3 not only provides performance improvements, but also uses new features such as a combination API to improve code organization and maintainability. While the migration process may be challenging, the long-term benefits are worth investing.

The above is the detailed content of the tutorial and pit avoidance guide for upgrading Vue2 to Vue3. For more information about upgrading Vue2 to Vue3, please follow my other related articles!