首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >我用Vue2写了五年,转Vue3后悔了吗?

我用Vue2写了五年,转Vue3后悔了吗?

原创
作者头像
peace-free
发布2025-12-03 19:44:06
发布2025-12-03 19:44:06
5530
举报
文章被收录于专栏:Vue3Vue3

从Vue2一路走来,Vue3到底变了什么?值不值得升级?一个老Vue开发者的真实心路历程。


说实话,2020年Vue3刚发布的时候,我是有点抵触的。

用了五年Vue2,项目跑得好好的,组件库生态也成熟,干嘛要折腾?组合式API那一堆refreactive看着就头大,感觉是在写React。

但后来...真香。

这篇文章就是写给和当年的我一样的Vue2老用户的,聊聊Vue3到底改了什么,以及我为什么觉得值得升级。

Vue3官网首页
Vue3官网首页

Vue3官网:The Progressive JavaScript Framework

先说结论:后悔了吗?

不后悔,甚至有点相见恨晚。

但这个结论需要一些前提:

  • 如果你只是维护老项目,不升也没问题
  • 如果你要开新项目,强烈建议Vue3
  • 如果你想提升自己,Vue3必须学

下面展开说说为什么。

Vue2到底有什么问题?

用了这么久Vue2,其实也习惯了它的一些"小毛病"。但换到Vue3之后回头看,才发现这些问题确实挺膈应人的。

问题一:响应式的"缺陷"

Vue2用的是Object.defineProperty来实现响应式,这玩意儿有个先天缺陷——没法监听新增属性和数组索引变化。

代码语言:javascript
复制
// Vue2的日常
export default {
  data() {
    return {
      user: { name: '张三' }
    }
  },
  methods: {
    addAge() {
      // ❌ 这样写不会触发更新
      this.user.age = 25

      // ✅ 必须用$set
      this.$set(this.user, 'age', 25)
    },
    updateList() {
      // ❌ 这样改数组也不会更新
      this.list[0] = 'new value'

      // ✅ 还是得用$set
      this.$set(this.list, 0, 'new value')
    }
  }
}

每次写到这里我都想吐槽:凭什么给对象加个属性还要调方法?

问题二:代码组织的"分裂"

Vue2的选项式API把同一个功能的代码拆得七零八落。

比如我要实现一个"搜索功能",代码分布是这样的:

代码语言:javascript
复制
export default {
  data() {
    return {
      keyword: '',        // 搜索关键词在这
      results: [],        // 搜索结果在这
      loading: false      // 加载状态在这
    }
  },
  computed: {
    hasResults() {        // 计算属性在这
      return this.results.length > 0
    }
  },
  methods: {
    async search() {      // 搜索方法在这
      this.loading = true
      this.results = await api.search(this.keyword)
      this.loading = false
    }
  },
  watch: {
    keyword(newVal) {     // 监听器在这
      this.debouncedSearch()
    }
  },
  created() {             // 生命周期在这
    this.debouncedSearch = debounce(this.search, 300)
  }
}

一个搜索功能,代码分散在5个地方。组件简单还好,一旦复杂起来,你得上下来回跳着看代码,看得人眼花。

问题三:逻辑复用的"尴尬"

Vue2复用逻辑主要靠mixins,但mixins有几个硬伤:

代码语言:javascript
复制
// userMixin.js
export default {
  data() {
    return {
      user: null,
      loading: false
    }
  },
  methods: {
    fetchUser() { /* ... */ }
  }
}

// 组件中使用
export default {
  mixins: [userMixin, permissionMixin, logMixin],
  data() {
    return {
      loading: true  // 这个loading和mixin里的冲突了,谁赢?
    }
  }
}

问题来了:

  • this.user到底是哪个mixin的?看不出来
  • 多个mixin的属性名冲突怎么办?
  • mixin之间互相依赖怎么追踪?

我见过一个项目,一个组件引了8个mixin,出了bug根本不知道从哪找起。

Vue3怎么解决这些问题的?

解决方案一:Proxy响应式

Vue3换成了Proxy来实现响应式。Proxy可以拦截对象的所有操作,不管是读取、修改、新增还是删除。

代码语言:javascript
复制
// Vue3,直接写就完事了
const user = reactive({ name: '张三' })

// ✅ 新增属性,能响应
user.age = 25

// ✅ 删除属性,也能响应
delete user.name

// ✅ 数组索引,照样响应
const list = reactive(['a', 'b', 'c'])
list[0] = 'new value'

再也不用记什么时候该用$set了。这一点我觉得就值回票价。

image-20251203193325197
image-20251203193325197

解决方案二:组合式API

这是Vue3最大的变化。同样是搜索功能,用组合式API写是这样的:

代码语言:vue
复制
<script setup>
import { ref, computed, watch } from 'vue'
import { useDebounceFn } from '@vueuse/core'

// 搜索相关的代码全在一起
const keyword = ref('')
const results = ref([])
const loading = ref(false)
const hasResults = computed(() => results.value.length > 0)

async function search() {
  loading.value = true
  results.value = await api.search(keyword.value)
  loading.value = false
}

const debouncedSearch = useDebounceFn(search, 300)
watch(keyword, debouncedSearch)
</script>

一个功能的代码放在一起,不用在各个选项之间跳来跳去。

更重要的是,这些逻辑可以轻松抽取成可复用的函数:

代码语言:javascript
复制
// useSearch.js
export function useSearch(apiFn) {
  const keyword = ref('')
  const results = ref([])
  const loading = ref(false)

  async function search() {
    loading.value = true
    results.value = await apiFn(keyword.value)
    loading.value = false
  }

  const debouncedSearch = useDebounceFn(search, 300)
  watch(keyword, debouncedSearch)

  return { keyword, results, loading, search }
}
代码语言:vue
复制
<script setup>
import { useSearch } from './useSearch'

// 一行搞定,来源清晰
const { keyword, results, loading } = useSearch(api.searchUsers)
</script>

这比mixins清爽太多:

  • 你清楚地知道每个变量从哪来
  • 不会有命名冲突
  • TypeScript类型推导完美支持
image-20251203193452817
image-20251203193452817

解决方案三:更好的TypeScript支持

Vue3从底层就用TypeScript重写了,类型推导准确多了:

代码语言:vue
复制
<script setup lang="ts">
interface User {
  id: number
  name: string
  age?: number
}

// props有完整类型提示
const props = defineProps<{
  user: User
  readonly?: boolean
}>()

// emit也有类型约束
const emit = defineEmits<{
  (e: 'update', user: User): void
  (e: 'delete', id: number): void
}>()

// ref自动推导类型
const count = ref(0)  // Ref<number>
const user = ref<User | null>(null)  // Ref<User | null>
</script>

在VS Code里写Vue3 + TS,自动补全和错误提示都很准,体验好了一个档次。

还有这些你该知道的新东西

Teleport:弹窗终于不用写在外面了

以前写Modal,要么把组件放到body下,要么和z-index、overflow斗智斗勇。现在有Teleport:

代码语言:vue
复制
<template>
  <button @click="showModal = true">打开弹窗</button>

  <!-- 内容传送到body下,但逻辑还在这个组件里 -->
  <Teleport to="body">
    <div v-if="showModal" class="modal">
      <h2>我是弹窗</h2>
      <button @click="showModal = false">关闭</button>
    </div>
  </Teleport>
</template>

Fragment:终于不用包一层div了

代码语言:vue
复制
<!-- Vue2:必须有根节点 -->
<template>
  <div>
    <header>头部</header>
    <main>内容</main>
    <footer>底部</footer>
  </div>
</template>

<!-- Vue3:多个根节点OK -->
<template>
  <header>头部</header>
  <main>内容</main>
  <footer>底部</footer>
</template>

性能提升

Vue3在性能上做了很多优化,官方数据:

  • 包体积减少41%(tree-shaking支持)
  • 初次渲染快55%
  • 更新快133%
  • 内存占用减少54%
npm下载趋势
npm下载趋势

Vue的npm下载趋势持续增长

选项式API还能用吗?

能用,完全能用。

Vue3是向后兼容的,选项式API在Vue3里一样跑得好好的:

代码语言:vue
复制
<script>
// 这种写法在Vue3里完全没问题
export default {
  data() {
    return { count: 0 }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>

你不需要一次性把所有代码都改成组合式API,可以慢慢来。新功能用组合式,老代码不动它,完全可以。

升级需要注意的坑

变更项

Vue2

Vue3

创建应用

new Vue()

createApp()

v-model默认prop

value

modelValue

v-model默认事件

input

update:modelValue

事件总线

$on, $off, $once

移除,用mitt等

过滤器

{{ msg \| capitalize }}

移除,用computed

过渡class

v-enter

v-enter-from

完整迁移指南:Vue3迁移文档

所以,该升级吗?

新项目:毫无疑问选Vue3。Vue2已经在2023年底停止维护了。

老项目

  • 如果项目稳定、不怎么迭代,可以不动
  • 如果还在活跃开发,建议找机会升级
  • 先在新功能/新页面试水,逐步迁移

个人技能:不管项目升不升,Vue3你都该学。这是Vue的未来方向,面试也会问。

我的学习建议

作为Vue2老用户,建议按这个顺序学:

  1. 先跑起来:用Vite创建个Vue3项目,感受下秒级启动
  2. 响应式API:ref、reactive搞明白,这是基础中的基础
  3. 组合式API:学会用setup组织代码
  4. script setup:这是现在的标准写法
  5. 配套升级:Vue Router 4、Pinia

小结

回到开头的问题:用了五年Vue2,转Vue3后悔了吗?

不后悔。

Vue3解决了Vue2的几个核心痛点:

  1. 响应式不再有限制,告别$set
  2. 组合式API让代码组织更合理
  3. TypeScript支持大幅提升
  4. 性能更好,包更小

当然,学习曲线是有的。但跨过去之后,你会发现真的香。


下一篇预告:Vue3生态全景图:除了Vue,你还需要认识这些朋友

Vite、Pinia、VueUse、Element Plus... Vue3全家桶都有谁?下一篇带你认识。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 先说结论:后悔了吗?
  • Vue2到底有什么问题?
    • 问题一:响应式的"缺陷"
    • 问题二:代码组织的"分裂"
    • 问题三:逻辑复用的"尴尬"
  • Vue3怎么解决这些问题的?
    • 解决方案一:Proxy响应式
    • 解决方案二:组合式API
    • 解决方案三:更好的TypeScript支持
  • 还有这些你该知道的新东西
    • Teleport:弹窗终于不用写在外面了
    • Fragment:终于不用包一层div了
    • 性能提升
  • 选项式API还能用吗?
  • 升级需要注意的坑
  • 所以,该升级吗?
  • 我的学习建议
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档