Vue.js 踩坑记:从 React 转型后的真实项目实战
去年底,我们小厂业务线接到一个“紧急又重要”的任务——重构内部运营后台。作为团队里唯一一个后端(兼半个前端),我原本以为又是套个 Ant Design Pro 交差了事。结果产品经理甩过来一句:“这次要用 Vue3,UI 风格对标 Notion。”我当时就懵了:我可是 Vim 党,连 VS Code 都很少开,更别说天天和 Composition API 打交道了。
但没办法,老板说“技术要多元化”,加上隔壁组用 React 写的系统老是内存泄漏,运维半夜打电话骂人,领导一拍脑袋:“试试 Vue 吧,听说更轻量。”
为啥不继续用 React?
说实话,我对 React 并无偏见。之前维护的老系统就是 React + Redux,写得还挺顺手。但问题也明显:
- Bundle 太大:光 React + ReactDOM 就 120KB+,gzip 后还是比 Vue 大一圈
- 状态管理复杂:Redux 写起来像在填表格,每个 action 都要定义 type、payload、reducer,改个字段要改五处
- SSR 配置反人类:上次搞服务端渲染,webpack 配置改到凌晨三点,最后发现是 babel-loader 没加 target: 'node'
而 Vue 的卖点很实在:渐进式、文档友好、单文件组件直观。对我们这种人力紧张的小团队来说,上手快比什么都重要。
实战:从零搭起一个可维护的 Vue3 项目
1. 技术栈选型:别贪多,够用就行
我们最终定了这套组合:
- 框架:Vue 3.4(Composition API)
- 构建工具:Vite(告别 webpack 的 30 秒启动)
- UI 库:Naive UI(TypeScript 支持好,主题定制灵活)
- 状态管理:Pinia(比 Vuex 简洁太多)
- 路由:Vue Router 4
- HTTP 客户端:Axios(虽然 fetch 原生,但拦截器太香)
💡 小厂经验:别一上来就搞微前端、Monorepo。先跑起来,再优化。我们连 Eslint 都是第二周才加的——第一周光赶 deadline 了。
2. 目录结构:让新人三天内能看懂
src/
├── api/ # 接口定义
├── assets/ # 静态资源
├── components/ # 通用组件
├── composables/ # 自定义组合函数(重点!)
├── layouts/ # 布局组件
├── pages/ # 页面级组件
├── router/ # 路由配置
├── stores/ # Pinia store
├── types/ # TS 类型定义
├── utils/ # 工具函数
└── App.vue
关键设计:把 composables 单独拎出来。比如用户权限校验,我写了个 useAuth.ts:
// composables/useAuth.ts
import { ref, computed } from 'vue'
import { useUserStore } from '@/stores/user'
export function useAuth() {
const userStore = useUserstore()
const hasPermission = computed(() => (perm: string) => {
return userStore.permissions.includes(perm)
})
// 返回响应式引用,模板里直接用
return { hasPermission }
}
在页面里调用:
<script setup>
import { useAuth } from '@/composables/useAuth'
const { hasPermission } = useAuth()
</script>
<template>
<button v-if="hasPermission('delete')">删除</button>
</template>
效果:逻辑复用率提升 70%,测试也方便——直接 mock useUserStore 就行。
和 React 的综合对比:不是谁更好,而是谁更适合
我知道肯定有人要问:“Vue 和 React 到底哪个强?”作为两边都踩过坑的人,我的结论很朴素:
| 维度 | Vue 3 | React 18 |
|---|---|---|
| 上手速度 | ⭐⭐⭐⭐⭐(模板直观) | ⭐⭐⭐(JSX 需适应) |
| 状态管理 | Pinia 简洁,无需 middleware | Redux Toolkit 还行,但配置多 |
| 性能 | 编译时优化,更新更精准 | Fiber 可中断,但 diff 成本高 |
| TypeScript 支持 | 原生支持,类型推导强 | 需额外配置,泛型容易翻车 |
| 生态成熟度 | 中后台够用,复杂交互略弱 | 生态无敌,但碎片化严重 |
| 团队协作 | 模板分离,前端/切图更容易介入 | 全 JS,对非 JS 开发者不友好 |
🤔 个人吐槽:React 社区总在造新轮子(Zustand、Jotai、Recoil…),Vue 社区相对稳定。对我们这种没时间追新的人,稳定就是生产力。
踩过的坑:那些让我想砸键盘的瞬间
坑 1:响应式丢失 —— ref vs reactive
一开始我把所有状态都用 reactive 包:
const state = reactive({ list: [] })
// 在 API 回调里
state.list = res.data // OK
但后来需要把 list 传给子组件,结果子组件修改无效!因为 reactive 解包后失去响应性。
正确做法:用 ref 包数组/对象,或者在传递时用 toRefs。
// 父组件
const list = ref([])
// 子组件 props
defineProps<{ list: Ref<Item[]> }>()
💡 教训:简单数据用
ref,复杂嵌套对象才考虑reactive。别为了少写.value而牺牲可维护性。
坑 2:Vite + Axios 的跨域代理
开发时接口在 http://api.internal.com,本地启 Vite 服务在 localhost:3000,直接请求 CORS 报错。
错误配置(网上抄的):
// vite.config.js
server: {
proxy: {
'/api': 'http://api.internal.com'
}
}
结果请求变成 GET /api/users → 转发到 http://api.internal.com/api/users,但后端路径是 /users!
正确配置:
server: {
proxy: {
'/api': {
target: 'http://api.internal.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
然后 Axios 基地址设为 /api,完美解决。
性能优化:小厂也要讲究用户体验
虽然只是内部系统,但加载慢了运营小姐姐会骂人。我们做了几件事:
- 路由懒加载:
// router/index.ts const Dashboard = () => import('@/pages/Dashboard.vue') - 图片懒加载:用
IntersectionObserver自制指令 - 列表虚拟滚动:超过 100 条数据就上
vue-virtual-scroller - 缓存计算结果:用
computed代替方法调用
最惊喜的是 Vite 的 HMR 速度——改一行 CSS,浏览器 50ms 内更新,再也不用等 webpack 的“92% additional asset processing”。
最后:为什么我觉得 Vue 适合小团队?
写这篇文章时,已经是上线后第三个月。系统稳定运行,连测试同学都说“这次 Bug 少多了”。回顾整个过程,Vue 给我的最大感受是:它不炫技,但务实。
- 模板语法让 HTML/CSS 开发者也能参与
- 单文件组件天然隔离关注点
- 文档示例即拷即用,不用翻 GitHub issue
- 生态工具链成熟(Volar、Vue Devtools)
当然,如果是超大型应用或需要极致灵活性(比如做设计器),React 可能更合适。但对我们这种三个人维护五条业务线的小厂来说,Vue 的“约定优于配置”省下了大量沟通和调试成本。
一点私货:后端眼中的前端未来
最近我在业余时间研究 Rust,发现前端和系统编程有个共同趋势:类型安全 + 编译时优化。Vue 3 的 <script setup> + TypeScript,其实就是在往这个方向走——用编译器提前发现问题,而不是靠 runtime 报错。
也许不久后,我们会看到更多像 Svelte、Solid.js 这样“编译时框架”的崛起。但无论如何,工具只是手段,解决问题才是目的。
对了,上周产品经理又提了新需求:“能不能加个暗黑模式?”
我笑了笑,打开 naive-ui 的文档,5 分钟搞定。
——这才是小厂程序员的快乐。
📌 彩蛋:如果你也在用 Vim 写 Vue,推荐插件
vim-vue+coc-volar,补全体验接近 IDE。虽然偶尔还是会手滑按成:wq关掉浏览器标签页……

评论 0