从零开始构建一个现代化前端项目:一个传统企业Java仔的深夜自救指南

★许思涵
2025-12-16 01:48
阅读 707

上周五晚上十点半,我坐在工位上,盯着屏幕上那个被产品经理临时塞进双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

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