从零开始构建一个现代化前端项目:一个传统企业Java仔的深夜自救指南
上周五晚上十点半,我坐在工位上,盯着屏幕上那个被产品经理临时塞进双11大促需求里的“数据看板”原型图,差点把咖啡泼到键盘上。作为一个在传统制造企业干了五年后端的Java开发,我最近不仅要维护那堆祖传的Spring Boot服务,还要被拉去“支援”前端——理由是:“你不是懂点JS吗?”
更离谱的是,这玩意儿还得集成一个内部爬虫抓取竞品价格的功能(别问为什么用爬虫,问就是业务部门说“Excel太慢了”)。而我,一个平时只和MyBatis、Redis、Kafka打交道的男人,突然要从零搭一个现代化前端项目,还要求“性能好、体验佳、上线快”。
行吧,反正我也在准备跳槽,简历上总不能只写“精通CRUD”和“能背八股文”。不如趁这个机会,把Vue 3 + Vite + TypeScript这套现代前端全家桶真正跑通一遍。于是,我在凌晨一点打开了VS Code,开始了这场名为“代码人生”的修行。
别再用Webpack了!Vite真香警告
以前公司老项目还在用Webpack 4,改个样式要等20秒热更新,每次调试都像在坐牢。这次我直接上了 Vite —— 毕竟人家号称“闪电般快速”,而且和Vue官方深度绑定。
npm create vue@latest my-dashboard
一路回车选TypeScript、Pinia、Router、ESLint、Prettier,三秒建完项目。启动 npm run dev,浏览器秒开,改一行代码,页面唰一下就变了。那一刻我差点哭出来:原来前端开发可以不痛苦?
吐槽时间:我们运维老哥听说我要换构建工具,一脸警惕:“Vite?没听过,稳定吗?线上出问题谁背锅?” 我只能默默回他:“比你上次误删生产数据库日志稳多了。”
性能优化:不是炫技,是保命
领导的要求很朴素:“打开要快,滑动要顺,别让用户骂娘。” 但现实很骨感:我们的用户很多还在用三年前的安卓机,Chrome版本停留在89,甚至有人用IE模式(别笑,真有)。
所以,性能优化不是加分项,是生死线。
1. 路由懒加载:别一次性把全家桶塞给用户
一开始我把所有页面组件全 import 进来,首屏 JS 包飙到 3MB。测试同事直接在群里@我:“首页加载转圈30秒,用户以为网站挂了。”
赶紧改成动态导入:
const PriceDashboard = () => import('@/views/PriceDashboard.vue')
const CrawlerMonitor = () => import('@/views/CrawlerMonitor.vue')
const routes = [
{ path: '/dashboard', component: PriceDashboard },
{ path: '/crawler', component: CrawlerMonitor }
]
结果首包体积直接砍掉60%,Lighthouse评分从45飙到82。
2. 图片和静态资源:压缩!懒加载!
产品非要放一堆高清大图展示“高端大气”,我说:“您这图比我的年终奖还重。” 最后妥协方案:用 v-lazy 插件 + WebP 格式 + CDN 压缩。
<img v-lazy="productImage" :alt="productName" loading="lazy" />
顺便配置了 Vite 的 vite-plugin-imagemin,自动压缩图片,构建时省了1.2MB。
3. 防抖节流:别让爬虫数据刷爆你的UI
爬虫后台每5秒推送一次新数据,如果直接 watch 数据变化并重绘图表,低端机直接卡成PPT。我加了个防抖:
import { debounce } from 'lodash-es'
const updateChart = debounce((data) => {
chartInstance.setOption({ series: [{ data }] })
}, 300)
用户体验立马丝滑,连测试都说:“这次滑动不卡了,是不是偷偷换了服务器?”
爬虫集成:前端也能玩“数据采集”?
说起来有点魔幻——一个前端项目,居然要对接内部爬虫服务。后端提供了一个 WebSocket 接口,实时推送抓取到的价格数据。
最初我傻乎乎地直接在 onMounted 里连 WebSocket,结果页面一刷新,连接数暴涨,运维又来敲门:“你们前端是不是在DDoS我们爬虫服务?”
后来改成了 按需连接 + 自动重连 + 心跳检测:
class CrawlerSocket {
private ws: WebSocket | null = null
private reconnectTimer: number | null = null
connect() {
this.ws = new WebSocket('wss://crawler.internal/ws')
this.ws.onmessage = (e) => {
const data = JSON.parse(e.data)
// 通过 Pinia store 更新状态
useCrawlerStore().updateData(data)
}
this.startHeartbeat()
}
// 省略重连和心跳逻辑...
}
关键点:页面隐藏时断开连接,显示时重连。用了 document.visibilityState 监听:
watchEffect(() => {
if (document.visibilityState === 'visible') {
crawlerSocket.connect()
} else {
crawlerSocket.disconnect()
}
})
这下不仅省了服务器资源,还避免了用户切到后台时还在疯狂拉数据。
兼容性:向IE时代的幽灵低头
虽然嘴上说着“谁还用IE”,但客户现场偏偏有台Windows 7 + IE11 的“古董机”。领导一句话:“必须兼容。”
我内心OS:这还不如让我手写汇编。
但没办法,用 @vitejs/plugin-legacy 打包两份代码,一份给现代浏览器,一份给IE:
// vite.config.ts
import legacy from '@vitejs/plugin-legacy'
export default defineConfig({
plugins: [
vue(),
legacy({
targets: ['defaults', 'IE 11']
})
]
})
同时,在 index.html 里加上 polyfill CDN:
<script nomodule src="https://polyfill.io/v3/polyfill.min.js?features=es2015%2Ces2016%2Ces2017"></script>
虽然最终IE版体验还是卡,但至少能打开——这就够了。毕竟,客户的KPI比用户体验重要(手动狗头)。
构建与部署:让CI/CD不再只是摆设
以前前端打包全靠手动 npm run build,然后 FTP 上传,经常覆盖错文件。这次我拉着运维搞了个 GitLab CI 流水线:
stages:
- build
- deploy
build-frontend:
stage: build
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
deploy-prod:
stage: deploy
script:
- rsync -avz dist/ user@prod-server:/var/www/dashboard
only:
- main
现在只要合并到 main 分支,自动构建+部署。再也不用半夜被叫起来“修复线上样式错乱”了。
成果与感悟:代码人生,不止于CRUD
项目上线一周,老板在周会上夸“前端体验提升明显”,产品经理也难得没提新需求(奇迹!)。更重要的是,我自己收获了一套可复用的现代化前端脚手架,还顺手整理了一份《传统企业前端避坑指南》。
| 优化项 | 优化前 | 优化后 | 提升效果 |
|---|---|---|---|
| 首屏加载时间 | 8.2s | 1.9s | ↓77% |
| Bundle体积 | 3.1MB | 1.2MB | ↓61% |
| Lighthouse评分 | 45 | 88 | ↑96% |
| 用户跳出率 | 63% | 29% | ↓54% |
现在每天深夜写代码时,我不再只是机械地调接口、改Bug。而是会思考:这个交互是否流畅?这个动画是否多余?这个请求能否合并?
也许这就是“代码人生”的另一面——不只是为了交付需求,更是为了做出让人用起来舒服的产品。
顺便,我的跳槽简历上终于能写:“主导从0到1构建高性能前端项目,集成实时数据爬虫,首屏加载<2s”。HR看了都说好。
最后送大家一句我在调试第37次WebSocket断连时悟出的真理:
前端性能优化,90%靠克制,10%靠技术。别让用户为你的任性买单。
好了,凌晨两点,该去刷LeetCode了。明天还要面对产品经理的新需求:“能不能加个AI智能推荐?”…… 我先去续杯咖啡。

评论 0