Vue.js生态实战:从项目重构到GitHub开源的一路踩坑
上周五晚上九点半,我在国贸地铁站拖着疲惫的身体往家走——又是一天和Vue组件死磕的日子。作为一个天天调参炼丹的AI算法工程师,按理说前端不该是我的主战场。但自从去年我们团队接了个内部可视化平台项目,领导一句“你不是懂点前端嘛”,我就被迫重拾Vue,结果一发不可收拾。
这半年下来,我不仅把Vue3 + TypeScript + Vite这套组合拳打得滚瓜烂熟,还顺手在GitHub上开源了一个轻量级UI组件库。今天这篇技术分享,就是想把这一路踩过的坑、优化过的性能、调教过的构建流程,原原本本地记录下来。希望对正在Vue生态里摸爬滚打的兄弟们有点帮助。
起因:一个被产品经理“逼”出来的重构
事情要从去年双11说起。当时我们组要给运营团队做一个实时数据看板,展示大促期间的模型效果指标。初版是用Vue2 + Webpack搭的,赶工上线后勉强能用,但体验相当拉胯:
- 首屏加载超过5秒(运营小姐姐当场翻白眼)
- 切换Tab时卡顿明显(“你们这页面是不是卡了?”)
- 构建时间长达3分钟(每次改个颜色都要喝杯咖啡等)
更糟的是,随着需求不断叠加,代码逐渐变成意大利面条——父子组件通信靠$emit传三代,状态管理全靠props层层透传,Vuex store里塞满了各种临时变量。
终于有一天,产品经理拍着桌子说:“能不能像Ant Design Pro那样丝滑?人家那个Table切换数据都不带卡的!”
行吧,重构就重构。反正我也受够了Webpack那慢如蜗牛的HMR(Hot Module Replacement)。
技术选型:为什么是Vue3 + Vite?
说实话,一开始我也犹豫过要不要直接上React。毕竟我们团队日常写PyTorch,JSX那一套逻辑更贴近函数式思维。但考虑到现有代码都是Vue,且团队里有老哥对Vue有执念,最终还是决定拥抱Vue3。
核心选型理由如下:
- Composition API:逻辑复用比Options API清晰太多,尤其适合我们这种复杂交互场景
- Vite的闪电构建:开发服务器启动<500ms,HMR更新基本无感
- TypeScript原生支持:再也不用担心props类型对不上导致运行时报错
- Tree-shaking更彻底:打包体积比Vue2小30%左右(实测数据)
于是,一个周末,我新建了仓库,命名为 vue-dashboard-pro,并默默推到了GitHub。没想到后来居然收获了200+ star,也算意外之喜。
实战一:状态管理的优雅解法
旧项目里,Vuex的store长得像一锅乱炖:
// 旧Vuex store(不忍直视)
state: {
selectedDate: null,
tableData: [],
isLoading: false,
errorMsg: '',
chartConfig: {},
userPreferences: {}, // 甚至把用户偏好也塞进来了...
}
重构时我做了两件事:
- 拆分模块:按功能域划分store(
dashboard.ts,table.ts,chart.ts) - 拥抱Pinia:Vue官方推荐的新一代状态管理库,TypeScript支持极佳
Pinia的写法简直清爽:
// stores/table.ts
import { defineStore } from 'pinia'
export const useTableStore = defineStore('table', () => {
const data = ref<TableRow[]>([])
const loading = ref(false)
const pagination = reactive({ page: 1, size: 20 })
const fetchData = async () => {
loading.value = true
try {
data.value = await api.getTableData(pagination)
} finally {
loading.value = false
}
}
return { data, loading, pagination, fetchData }
})
在组件中使用:
<script setup lang="ts">
import { useTableStore } from '@/stores/table'
const tableStore = useTableStore()
onMounted(() => tableStore.fetchData())
</script>
效果:代码可读性提升80%,单元测试也更容易写(直接mock store即可)。更重要的是,再也不用在.vue文件顶部写一长串mapState, mapActions了。
实战二:性能优化——让60fps成为常态
最开始重构完,自测感觉流畅多了。但一拿到真实数据(10万行表格),Chrome DevTools的Performance面板直接给我上了一课:FPS掉到15!
问题定位
通过录制用户操作,发现瓶颈主要在:
- 虚拟滚动缺失:一次性渲染全部DOM节点
- 响应式数据过度监听:每个表格行都变成响应式对象
- 重复计算:filter/sort逻辑没做缓存
解决方案
1. 引入虚拟滚动
我对比了vue-virtual-scroller和vueuc两个库,最终选了后者——更轻量(gzip后仅4KB),且支持动态高度。
<template>
<VirtualList
:items="visibleData"
:item-height="56"
class="table-body"
>
<template #default="{ item }">
<TableRow :data="item" />
</template>
</VirtualList>
</template>
注:
visibleData是经过filter/sort后的数据,但只渲染可视区域内的行(通常20-30条)
2. 使用shallowRef避免深度响应式
对于纯展示型的大数据,不需要深度监听:
// 将原始数据转为非响应式
const rawData = shallowRef<TableRow[]>([])
// 在计算属性中处理
const visibleData = computed(() => {
return rawData.value
.filter(row => row.status === filterStatus)
.slice(0, 1000) // 先限制数量
})
3. 计算属性缓存
利用computed的缓存特性,避免重复执行昂贵操作:
const sortedData = computed(() => {
console.log('sorting...') // 这行只会打印一次(只要依赖不变)
return [...visibleData.value].sort((a, b) => a.id - b.id)
})
优化前后对比
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 首屏加载 | 5.2s | 1.8s | 65% ↓ |
| 表格滚动FPS | 15 | 58 | 287% ↑ |
| Bundle Size | 2.1MB | 1.4MB | 33% ↓ |
现在运营小姐姐终于不翻白眼了,甚至主动问:“这个页面能不能做成我们所有系统的模板?”
实战三:构建部署与GitHub Actions自动化
作为算法工程师,我对CI/CD本来没啥概念。但自从某次上线把生产环境搞挂了(别问,问就是npm run build忘加--mode production),我就下定决心搞一套自动化流程。
Vite配置优化
首先调整vite.config.ts:
export default defineConfig({
build: {
target: 'es2015',
minify: 'terser',
terserOptions: {
compress: {
drop_console: true, // 自动移除console.log
drop_debugger: true
}
},
rollupOptions: {
output: {
manualChunks: {
// 分离第三方库
vendor: ['vue', 'pinia', 'axios'],
chart: ['echarts']
}
}
}
}
})
GitHub Actions流水线
在.github/workflows/deploy.yml中配置:
name: Deploy to Production
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install deps
run: npm ci
- name: Build
run: npm run build
- name: Deploy to OSS
uses: manyuanrong/setup-ossutil@master
with:
endpoint: ${{ secrets.OSS_ENDPOINT }}
access-key-id: ${{ secrets.OSS_KEY_ID }}
access-key-secret: ${{ secrets.OSS_KEY_SECRET }}
run: ossutil cp -r dist/ oss://my-bucket/dashboard/
现在每次合并到main分支,代码会自动构建并上传到阿里云OSS,配合CDN,全球访问速度飞起。再也不用半夜被运维电话叫醒:“你上次上线的静态资源404了!”
开源分享:把轮子造得漂亮一点
做完内部项目后,我觉得有些通用组件(比如那个高性能表格、日期范围选择器)值得抽象出来。于是花了一周时间,剥离业务逻辑,写了完善的文档和Demo,发布了vue-dashboard-ui到GitHub。
没想到收获了不少issue和PR。有个老哥甚至贡献了暗色主题,让我感动得差点流泪(程序员的眼泪很贵的好吗)。
开源过程中有几个心得:
- 文档要详细:哪怕多花半天写README,也能省下无数解释的时间
- Demo要可运行:最好提供StackBlitz或CodeSandbox链接
- TypeScript定义必须完善:前端开发者现在很看重这个
- 遵循SemVer版本规范:别动不动就breaking change
现在这个库已经成了我们团队新项目的标配,连隔壁Java组都在用。看来“不要重复造轮子”的前提,是你造的轮子得足够圆。
最后:Vue生态的思考
折腾完这一通,我对Vue生态有了更深的理解:
- Vite确实是未来:开发体验碾压Webpack,尤雨溪诚不我欺
- 组合式API更适合复杂逻辑:尤其是需要跨组件复用的场景
- 生态工具链日趋成熟:从状态管理(Pinia)到测试(Vitest),Vue3的配套已经非常完善
当然,Vue也不是银弹。比如在超大型应用中,React的生态可能更丰富;对于需要极致性能的场景,Svelte或许更合适。但就我们这种中后台系统而言,Vue3 + TypeScript + Vite的组合,真的香。
写这篇文章的时候,窗外北京的夜色正浓。回想这半年,从被产品经理追着问“页面怎么这么卡”,到如今项目稳定运行、GitHub收到感谢issue,虽然过程充满debug的痛苦,但那种“搞定它”的成就感,大概就是程序员继续搬砖的动力吧。
如果你也在Vue的路上踩坑,欢迎来我的GitHub repo提issue——说不定你的问题,正是我明天要解决的。毕竟,技术分享的意义,不就在于互相照亮前行的路吗?
(完)
P.S. 本文所有代码示例均已脱敏,真实项目中的报错信息比这惨烈十倍。比如那个著名的“Cannot read property 'map' of undefined”,我现在听到都会PTSD……

评论 0