从 Vue2 到 Vue3:一个老Vue开发者的升级指南
最近和前端同事交流发现在 vue3 的项目中还是惯性的使用 vue2 的选项式写法。这篇文章将从实践角度分享我的学习心得,希望能帮助同样准备升级的你。
为什么要升级到 Vue3?
在开始之前,我们先聊聊为什么要升级到 Vue3:
- 更好的性能:Vue3 的虚拟 DOM 重写和 Tree-shaking 支持带来了更好的性能
 - 组合式 API:解决了 Vue2 中组件逻辑复用的痛点
 - 更好的 TypeScript 支持:Vue3 是用 TypeScript 重写的,提供了更好的类型推导
 - 更小的包体积:得益于 Tree-shaking,你可以只打包用到的功能
 
快速上手指南
1. 创建应用的新方式
如果你习惯了 Vue2 的写法:
// Vue2 的写法
import Vue from 'vue'
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
在 Vue3 中变成了:
// Vue3 的写法
import { createApp } from 'vue'
const app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app')
个人感受:这种改变实际上更符合直觉,创建应用然后挂载插件的方式更加线性和清晰。
2. 响应式系统的变化
在 Vue2 中,我们习惯了直接在 data 中定义数据:
// Vue2
export default {
  data() {
    return {
      count: 0,
      user: {
        name: '张三',
        age: 25
      }
    }
  }
}
Vue3 中需要使用 ref 或 reactive:
// Vue3
  {{ count }}
  {{ user.name }}
个人最佳实践:
- 优先使用 ref,因为它可以包装任何值
 - 只在确实需要对象响应式的场景使用 reactive
 - 记住在 JS 中需要用 .value 访问 ref 的值
 
3. 组合式 API 介绍
在 Vue3 中,我们可以用 Composition API 重写:
// Vue3 的 组合式 API
为什么我推荐 Composition API:
- 相关逻辑可以组织在一起,不再需要在 data、methods、computed 之间来回跳转
 - 更好的代码复用能力,可以轻松提取和复用逻辑
 - 更好的类型推导支持
 
4. 模板语法的变化
Vue3 在模板语法方面保持了大部分与 Vue2 的兼容性,但有一些值得注意的改进:
  
    
    
      {{ item.name }}
    
    
    
    
       
      
      
    
  
  
   
  
    
    
      {{ item.name }}
    
   
  
主要改进:
- 支持多根节点模板
 - v-for 和 v-if 优先级更明确
 - 更好的 TypeScript 支持
 - 更好的性能优化
 
5. 生命周期的变化
Vue2 的生命周期钩子都有对应的 Vue3 版本,只是需要手动导入:
// Vue3
import { 
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted 
} from 'vue'
// 使用方式
onMounted(() => {
  console.log('组件已挂载')
})
注意:
- beforeCreate 和 created 钩子在 setup 中是不需要的,因为 setup 本身就是在这两个钩子之间执行
 - destroyed 改名为 unmounted
 - beforeDestroy 改名为 beforeUnmount
 
6. 计算属性的使用
计算属性在 Vue3 中的写法更加简洁:
// Vue2
export default {
  data() {
    return {
      firstName: '张',
      lastName: '三'
    }
  },
  computed: {
    // 只读计算属性
    fullName() {
      return this.firstName + this.lastName
    },
    // 可写计算属性
    fullNameWithSetter: {
      get() {
        return this.firstName + this.lastName
      },
      set(newValue) {
        [this.firstName, this.lastName] = newValue.split(' ')
      }
    }
  }
}
// Vue3
7. 监听器的改进
Vue3 的 watch 和 watchEffect 提供了更强大的监听能力:
// Vue2
export default {
  data() {
    return {
      name: '',
      userInfo: {
        age: 25
      }
    }
  },
  watch: {
    // 监听简单属性
    name(newVal, oldVal) {
      console.log('name changed:', newVal, oldVal)
    },
    // 监听对象属性
    'userInfo.age': {
      handler(newVal, oldVal) {
        console.log('age changed:', newVal, oldVal)
      },
      deep: true,
      immediate: true
    }
  }
}
// Vue3
8. Props 和 Emits 的声明
Vue3 提供了更明确的 Props 和 Emits 声明方式:
// Vue2
export default {
  props: {
    title: {
      type: String,
      required: true,
      default: ''
    }
  },
  methods: {
    handleClick() {
      this.$emit('update', { value: 'newValue' })
    }
  }
}
// Vue3
Props 和 Emits 的主要改进:
- 更好的类型推导
 - 运行时验证
 - 更清晰的事件类型定义
 - 可以使用 TypeScript 的类型标注
 
9. 插槽(Slots)的使用变化
// Vue2
  
    
     
    
    
     
    
    
     
  
// Vue3
  
   
  
  
   
  
  
   
  
  默认内容
  头部内容
  
    {{ data }}
  
 
10. Teleport 组件
Vue3 新增了 Teleport 组件,可以将内容渲染到 DOM 的其他位置:
  
11. 异步组件的改进
// Vue2
const AsyncComp = () => ({
  component: import('./AsyncComp.vue'),
  loading: LoadingComponent,
  error: ErrorComponent,
  delay: 200,
  timeout: 3000
})
// Vue3
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent({
  loader: () => import('./AsyncComp.vue'),
  loadingComponent: LoadingComponent,
  errorComponent: ErrorComponent,
  delay: 200,
  timeout: 3000
})
// 简写方式
const AsyncComp = defineAsyncComponent(() => import('./AsyncComp.vue'))
12. 全局 API 的改变
// Vue2
import Vue from 'vue'
Vue.config.ignoredElements = [/^app-/]
Vue.use(/* ... */)
Vue.mixin(/* ... */)
Vue.component(/* ... */)
Vue.directive(/* ... */)
// Vue3
import { createApp } from 'vue'
const app = createApp(App)
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('app-')
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)
13. 自定义指令的变化
// Vue2
Vue.directive('highlight', {
  bind(el, binding) {},
  inserted(el, binding) {},
  update(el, binding) {},
  componentUpdated(el, binding) {},
  unbind(el, binding) {}
})
// Vue3
app.directive('highlight', {
  beforeMount(el, binding) {}, // 替代 bind
  mounted(el, binding) {},     // 替代 inserted
  beforeUpdate(el, binding) {}, // 新增
  updated(el, binding) {},     // 替代 update + componentUpdated
  beforeUnmount(el, binding) {}, // 新增
  unmounted(el, binding) {}    // 替代 unbind
})
14. 过渡动画的变化
  hello
 
  hello
 
主要变化:
- 过渡类名改变:v-enter 改为 v-enter-from
 - 组件名称大写:transition 改为 Transition
 - 新增 persisted 钩子用于处理缓存组件的过渡
 
性能优化的改进
Vue3 在性能方面有显著提升:
- 更好的 Tree-shaking 支持
 - 按需引入 API
 - 更小的打包体积
 - Fragment 支持
 - 不再需要根节点包裹
 - 减少 DOM 层级
 - 静态提升
 - 静态节点被提升到渲染函数之外
 - 减少重复创建开销
 - Proxy 响应式系统
 - 更好的性能
 - 更完善的响应式支持
 
升级建议
- 渐进式迁移
 - 新功能用 Vue3 + Composition API
 - 老项目可以继续使用 Options API,Vue3 完全兼容
 - 随着对 Composition API 的熟悉,逐步重构老代码
 - 使用
 - 这是 Vue3 的推荐写法
 - 减少了模板引用的复杂度
 - 提供了更好的开发体验
 - TypeScript 支持
 - 即使不用 TypeScript,也建议看看类型定义
 - 配合 VS Code,可以获得很好的类型提示
 - 响应式系统使用建议
 - 优先使用 ref
 - 必要时使用 reactive
 - 注意 ref 的 .value 使用
 
结语
作为一个经历过从 Vue2 到 Vue3 迁移的开发者,我认为这次升级绝对值得。虽然学习曲线有点陡,但 Composition API 带来的好处远超过学习成本。建议大家在新项目中直接使用 Vue3,享受新版本带来的各种改进。
记住:Vue3 不是一个完全不同的框架,它是 Vue2 的自然进化,保持耐心,你会发现新的开发方式更加优雅和高效。
