从 Vue 2 到 Vue 3,我在大型项目中的进阶之路:Vue.js 生态系统深度探索与实战

产品和代码之间
2025-06-21 16:25
阅读 666

开篇:为什么我要写这篇文章?

开篇:为什么我要写这篇文章?

大家好,我是前端组的一个老码农,目前在一家中型互联网公司负责后台系统的开发维护工作。说到 Vue.js,其实我早在两三年前就开始用它了,那时候还是 Vue 2 的天下。现在我们公司在重构一个核心后台管理系统时,决定全面升级到 Vue 3,并引入 Vite + Composition API + Pinia 这一套生态组合。

这趟旅程并不轻松,过程中踩了很多坑,也积累了不少经验。今天就想通过这篇文章,和大家分享一下我在实际项目中使用 Vue.js 及其生态的一些实战经验和心得。希望对正在转型 Vue 3 或者准备深入掌握 Vue 生态体系的你有所帮助。


问题描述:旧项目的瓶颈与重构动机

问题描述:旧项目的瓶颈与重构动机

我们原来的系统是基于 Vue 2 + Element UI 搭建的一套后台管理平台,功能模块多、组件复用率高。随着业务增长,这个项目逐渐暴露出以下痛点:

  1. 代码结构臃肿:由于历史原因,大量的业务逻辑混杂在 methods 和生命周期钩子中,可读性差,后期难以维护。
  2. 状态管理混乱:Vuex 虽然解决了全局状态共享的问题,但在大型项目中,拆分 modules 后依然显得笨重,调试困难。
  3. 性能下降明显:页面打开缓慢,尤其是数据量大的表格页面,卡顿严重,严重影响用户体验。
  4. 兼容性和可拓展性不足:随着团队人数增加,新同学加入后上手慢,缺乏统一的技术规范;同时,老代码难以扩展新的业务需求。

面对这些挑战,我们决定做一次彻底的重构,目标是:

  • 升级至 Vue 3
  • 使用 Composition API 重构核心模块
  • 引入 Pinia 替代 Vuex
  • 使用 Vite 加速构建流程
  • 提升整体性能与可维护性

解决方案:技术选型与架构设计

在确定技术栈的时候,我们主要考虑了以下几点:

技术选型

工具 版本 原因
Vue 3.4.x Composition API 更清晰,TypeScript 支持更好
Vite 4.3.x 极快的冷启动速度,支持按需加载
Pinia 2.x 更简洁的状态管理,替代 Vuex 成为主流
TypeScript 5.x 类型安全 + 团队协作更高效
Vue Router 4.x Vue 3 官方配套
Element Plus 2.x Vue 3 兼容版,UI 组件丰富

用户交互流程图-1

架构设计思路

我们在架构设计上采用了以下策略:

  • 按模块划分结构:将整个系统划分为几个大功能区,每个区域为一个独立模块(如:订单中心、权限中心等),模块间尽量解耦。
  • Composition API 重构业务逻辑:将原来写在 methods 中的逻辑全部拆分成 composables 函数,提高复用率。
  • Pinia 状态管理优化:将原先的 Vuex modules 改写为多个 Pinia store,配合接口抽象化统一调用方式。
  • 性能优化手段
    • 所有表格组件按需加载
    • 对复杂计算进行缓存处理(比如使用 computed)
    • 接口请求去重(防重复请求)

代码实践:关键实现片段分享

一、Pinia Store 示例 —— 封装用户信息获取模块

以前在 Vuex 中要定义一个 user module 大概是这样:

// vuex/modules/user.js
export default {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

而在 Pinia 中我们可以这样实现:

// stores/userStore.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
import api from '@/api'

export const useUserStore = defineStore('user', () => {
  const userInfo = ref(null)

  async function fetchUserInfo() {
    try {
      const res = await api.getUserInfo()
      userInfo.value = res.data
    } catch (e) {
      console.error(e)
    }
  }

  return {
    userInfo,
    fetchUserInfo
  }
})

然后在组件里直接调用即可:

import { useUserStore } from '@/stores/userStore'

export default defineComponent({
  setup() {
    const userStore = useUserStore()
    
    onMounted(async () => {
      await userStore.fetchUserInfo()
    })

    return { userStore }
  }
})

是不是清爽多了?不需要再写 mapStatemapActions,也不需要区分 action 和 mutation,一切都变得自然得多。


二、Composition API 分离业务逻辑 —— 表格搜索封装

我们有一个订单管理页,里面包含复杂的筛选条件。之前的写法是在组件内部维护大量 data 和 methods,很难复用。现在改成 composition API 后,我们可以这样:

// composables/useOrderSearch.ts
import { ref, computed } from 'vue'

export function useOrderSearch() {
  const keyword = ref('')
  const filterStatus = ref(-1)

  const filters = computed(() => {
    return {
      keyword: keyword.value,
      status: filterStatus.value
    }
  })

  async function searchOrders(filters) {
    // 实际调用接口
    return await api.getOrderList(filters)
  }

  return {
    keyword,
    filterStatus,
    filters,
    searchOrders
  }
}

在组件中使用:

import { useOrderSearch } from '@/composables/useOrderSearch'

export default defineComponent({
  setup() {
    const { keyword, filterStatus, filters, searchOrders } = useOrderSearch()

    async function handleSearch() {
      const orders = await searchOrders(filters.value)
      updateTableData(orders)
    }

    return {
      keyword,
      filterStatus,
      handleSearch
    }
  }
})

这种“函数式组合”的写法让代码更清晰,也更容易测试和复用。


三、Vite 配置优化构建速度

我们之前用 Webpack,启动时间动辄 20s+,Vite 真的是救星。下面是我们的 vite.config.ts 片段:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import tsconfigPaths from 'vite-tsconfig-paths'

export default defineConfig({
  plugins: [
    vue(),
    tsconfigPaths(), // 支持 TS Path 映射
  ],
  server: {
    port: 3000,
    open: true
  },
  build: {
    outDir: 'dist',
    chunkSizeWarningLimit: 1000 // 控制打包体积告警
  },
  optimizeDeps: {
    include: ['element-plus'] // 提前预构建第三方依赖
  }
})

我们还用了 Vite 的 Auto Import 插件,减少手动 import。


踩坑经验:那些让我头秃的日子

✅ 1. Pinia 在 SSR 中遇到的问题

项目初期我们尝试接入 Vue 3 + Vite + SSR 渲染(主要是为了 SEO 优化部分营销页),结果发现 Pinia 的初始化在服务端运行会抛异常。

经过排查发现是因为服务器没有 window 对象,而某些 pinia 的 plugin 会访问 window。最终解决方案是将一些浏览器相关的逻辑抽出来,在服务端忽略执行。

💡 解决方案:

// main.ts(客户端)
const app = createApp(App)
app.use(pinia)
app.mount('#app')

// entry-server.ts(服务端)
import { createSSRApp } from 'vue'
const app = createSSRApp(App)
app.use(createPinia()) // 不注册某些插件
return { app }

✅ 2. TypeScript 中 Composable 泛型类型推导失败

我们在封装一个通用表格 hook 时,想传入一个泛型参数表示 item 类型:

function useTable<T>() {
  const list = ref<T[]>([])
  ...
}

但发现在使用的地方无法正确推导出类型,后来发现需要显式地传递类型才能被识别:

// 正确使用
const { list } = useTable<OrderItem>()

// 错误 ❌
const { list } = useTable() // T 默认为 unknown

这点需要注意,Vue 3 的 Composition API 和 TS 结合使用时,很多地方需要显式声明类型。


✅ 3. Element Plus 表格动态列渲染问题

在做一个列拖拽排序的表格配置器时,发现使用 v-if 控制列显示,会导致列宽错乱甚至空白不渲染。

后来发现原因是虚拟滚动机制导致 DOM 并未真正更新。于是改成了使用 v-show 替代 v-if,并在列切换时手动触发 resize:

<el-table-column
  v-for="col in columns"
  :key="col.prop"
  :prop="col.prop"
  :label="col.label"
  :show-overflow-tooltip="true"
  v-show="col.show"
/>

并在 change 事件中调用:

nextTick(() => {
  tableRef.handleResize() // 手动触发重新渲染
})

效果总结:重构后的收益

重构完成后,我们做了几项性能对比测试:

指标 Vue 2 + Webpack Vue 3 + Vite
构建时间 18s ~ 22s 2.8s ~ 4s
首屏渲染时间 3.2s 1.9s
包大小(压缩后) 3.1MB 2.2MB
代码行数 15W+ 12W+

重构后的效果非常显著,不仅提升了首屏加载速度,而且代码的结构更加清晰,团队协作效率也提高了。最关键的是,项目变得更加容易扩展,新产品需求可以在两周内快速上线。


经验分享:给前端同行们的建议

如果你也在考虑从 Vue 2 升级到 Vue 3,或者正准备搭建一个新的项目,这里有几个建议希望对你有用:

1. 📌 优先考虑 Composition API 的写法

Composition API 最大的优势不是写法本身,而是让你能更好地组织逻辑代码。比起 Options API,更能体现“关注点分离”,非常适合团队合作和长期维护。

2. ⚙️ 状态管理推荐用 Pinia,而非 Vuex

Vuex 在 Vue 3 中虽然还能用,但已经不再官方主推。Pinia 更轻量、语法更简单,而且可以配合 DevTools 很方便地调试。推荐作为首选状态管理工具。

3. 🔍 性能优化别只看包体积,也要关注“感知性能”

不要一味追求 bundle size,用户体验更重要。比如你可以先渲染骨架屏,再懒加载内容;使用 Suspense 组件提前占位等技巧,让用户感觉更流畅。

4. 🧪 写单元测试,尽早写,越早越好

我们在重构初期没怎么写测试,后来发现一旦涉及公共库改动,影响范围太大,只能靠手动回归测试。后来补上了 Jest + Vue Test Utils 的测试覆盖率,才真正安心。

5. 🔍 浏览器兼容性和调试小贴士

  • 使用 caniuse 查看是否支持 ESModule 功能
  • 给生产环境加个 Polyfill,比如 core-js 或 unplugin-vue-components 的自动注入
  • 推荐安装 Vue Devtools v6(Chrome 插件),支持 Composition API 跟踪和响应式追踪
  • 使用 Vue SFC Playground 快速验证模板或逻辑

写在最后:关于技术和成长的一些思考

回顾这段升级 Vue 3 的旅程,我觉得最深的感受就是——技术演进从来都不是一蹴而就的,它需要我们不断学习和拥抱变化

曾经我也很抗拒新技术,觉得学完就淘汰了怎么办?但现在我更愿意把它当作一种成长的过程。就像这次重构,虽然花了不少时间,但带来的不仅是性能提升,更是思维方式和工程能力上的跃迁。

如果你也遇到了类似的项目重构难题,不妨试试 Vue 3 的这套生态系统。它可能不会立刻解决所有问题,但它一定是一条值得走的路。

当然,欢迎你在评论区分享自己的经历或困惑,我们一起讨论,共同进步!


文末福利
如果你想了解完整的 Vue 3 + Vite + TypeScript + Pinia 的模板结构,可以关注我的 GitHub 仓库,我会陆续把我们项目中的最佳实践整理出来,欢迎 Star & Fork 😊

Happy Coding!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝