从零搭起一个现代前端项目:踩坑、工具与AI的奇妙化学反应
上周五晚上十点半,我正瘫在沙发上一边撸猫一边远程处理线上紧急 bug。产品那边刚发来消息:“老板看了竞品,说咱们页面加载太慢,下周三前必须优化完。”我叹了口气——这已经是我们小团队这个月第三次被“临时需求”暴击了。
作为这座三线城市里一家百人规模互联网公司的技术负责人,说实话,日常压力不小。公司没有大厂那种成熟的基建体系,很多东西得自己从头搭。好在我们团队氛围还算融洽,大家也都愿意折腾新技术。去年开始,公司终于意识到不能再用 jQuery 写新项目了,于是决定全面转向现代化前端架构。
但问题来了:从零开始构建一个“现代化”的前端项目,到底该怎么做?
不是照搬 Vite + React 就叫现代化
很多人觉得,用 create-react-app 或者 vite create 跑个命令,装几个依赖,就叫现代化项目。但现实很骨感——当你面对真实的业务场景时,你会发现光有框架远远不够。
比如我们最近做的一个 B 端管理后台,产品经理要求:
- 首屏加载 < 1.5s(哪怕用户用的是十年前的红米)
- 支持暗黑模式切换
- 组件要可复用到其他项目
- 必须有完善的类型校验和单元测试
- 后续要接入 AI 辅助功能(没错,就是现在最火的那个)
这些需求看似普通,但如果你直接拿脚手架开干,后期绝对会哭着重构。
所以我决定这次从零搭建,不依赖任何封装好的模板,把每一块都亲手配一遍。过程虽然痛苦,但收获巨大。
初始化:别急着写代码,先想清楚结构
我翻了翻书架上那本《前端架构:从入门到微前端》(人民邮电出版社那本,封面都快磨秃了),里面提到一个观点我很认同:项目结构决定了团队协作效率。
于是我们定了以下目录规范:
src/
├── assets/ # 静态资源
├── components/ # 通用组件(Button, Modal 等)
├── features/ # 业务模块(按功能划分,如 user, order)
├── hooks/ # 自定义 hooks
├── lib/ # 工具函数 & 第三方封装
├── routes/ # 路由配置
├── stores/ # 状态管理(我们用了 Zustand)
├── styles/ # 全局样式 & 主题变量
└── types/ # TypeScript 类型定义
为什么不用 pages?因为我们发现当项目变大后,pages 里塞太多逻辑会导致组件臃肿。拆成 features 后,每个功能模块自包含 UI、逻辑、状态、接口,更利于维护。
工具链选型:Vite 是底线,但不止于此
我们最终选择了 Vite + React 18 + TypeScript + Tailwind CSS 的组合。理由很简单:快、轻、生态好。
但真正让开发体验起飞的,是两个 AI 辅助工具:Codeium 和 通义千问。
Codeium:我的“代码 autocomplete Plus”
以前写个表单验证都要翻文档,现在装上 Codeium 插件后,它能根据上下文自动补全逻辑。比如我输入:
const validateEmail = (email: string) => {
它直接给我补全了正则校验和错误提示:
const validateEmail = (email: string) => {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email) ? null : '请输入有效的邮箱地址';
};
更离谱的是,它还能根据注释生成代码。我在组件顶部写:
// 实现一个防抖搜索框,延迟 300ms 触发 onSearch
它直接给我生成了完整的 useDebounceSearch hook!虽然有时会出错,但八成情况下能省我几分钟查文档的时间。
通义千问:不只是问答,更是“技术顾问”
有时候遇到复杂问题,比如“如何在 SSR 下正确处理 Tailwind 的动态类名”,我会直接问通义千问。它不仅能给出方案,还会解释原理。有一次我问:
“Vite 中如何配置多环境变量,且保证 .env 文件不被提交?”
它不仅给出了 import.meta.env 的用法,还提醒我设置 .gitignore,甚至附上了 CI/CD 中注入环境变量的示例。这种“带上下文思考”的能力,比 Stack Overflow 上零散的答案有用多了。
当然,我也吐槽过它:“你上次说的 Webpack 配置根本跑不起来!”但它下次就会学乖一点(笑)。
性能优化:不是加个懒加载就完了
回到开头那个“首屏加载 < 1.5s”的需求。我们做了几件事:
1. 路由级代码分割
const Dashboard = lazy(() => import('@/features/dashboard'));
const UserList = lazy(() => import('@/features/user'));
function App() {
return (
<Suspense fallback={<Spinner />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/users" element={<UserList />} />
</Routes>
</Suspense>
);
}
2. 图片懒加载 + WebP
用 next/image 太重,我们自己封装了一个 <LazyImage> 组件,结合 Intersection Observer API。同时让运维在 Nginx 层自动将 PNG/JPG 转 WebP(如果浏览器支持)。
3. 关键 CSS 内联
通过 vite-plugin-critical-css 插件提取首屏关键样式并内联到 HTML,避免 FOUC(Flash of Unstyled Content)。测试发现,Lighthouse 的 FCP(First Contentful Paint)从 2.3s 降到了 1.1s。
| 指标 | 优化前 | 优化后 |
|---|---|---|
| FCP | 2.3s | 1.1s |
| TTI | 3.5s | 1.8s |
| Bundle Size | 1.8MB | 820KB |
类型安全:TypeScript 不是摆设
很多团队把 TS 当成 JS 加个类型注解就完事了。但我们坚持:
- 所有 API 响应都用 Zod 校验 + 自动生成 TS 类型
- 组件 props 必须明确 interface
- 禁止使用
any(ESLint 强制报错)
比如用户接口:
import { z } from 'zod';
export const UserSchema = z.object({
id: z.number(),
name: z.string().min(1),
email: z.string().email(),
role: z.enum(['admin', 'user']),
});
export type User = z.infer<typeof UserSchema>;
这样前端拿到的数据一定是符合预期的,再也不用担心后端突然改字段导致页面白屏。
测试不能少:但别搞形式主义
我知道很多小公司觉得“测试浪费时间”。但我们吃过亏——去年双11前,一个没测的日期选择器组件,在 Safari 上直接崩了,导致订单页打不开。
现在我们强制要求:
- 所有 hooks 必须有单元测试(用 Vitest)
- 核心业务流程要有 E2E 测试(Playwright)
但不追求 100% 覆盖率。比如纯展示型组件,只要 props 类型对了,就不写测试。把精力花在刀刃上。
最后:现代化 ≠ 追新,而是解决问题
折腾这一圈下来,我最大的体会是:所谓“现代化前端项目”,核心不是用了多少新工具,而是能否高效、稳定地交付价值。
Vite 让我们启动快,TypeScript 减少低级错误,Tailwind 提升 UI 开发速度,而 Codeium 和通义千问则像两个不知疲倦的助手,帮我节省大量机械劳动。
当然,过程中也踩了不少坑:
- Tailwind 的 JIT 模式在 Docker 构建时缓存失效
- Vite 的 HMR 在 Windows WSL2 下偶尔失灵
- 通义千问有一次建议我用
eval()解析 JSON(差点酿成安全事故)
但每次解决一个问题,团队的技术债就少一分。上周三,我们准时上线了优化版,老板亲自发了个红包。那一刻,感觉熬的夜都值了。
如果你也在三线城市的小公司挣扎,想搞点“高大上”的技术却资源有限——别怕。从一个小项目开始,亲手配一遍工具链,用好 AI 助手,慢慢积累。现代化不是一蹴而就的工程,而是一次次踩坑后的自然进化。
对了,最近我在读《深入浅出 React 和 Redux》,打算结合 AI 写个系列笔记。如果你感兴趣,评论区喊一声,我抽空更新。
毕竟,程序员的浪漫,就是把混乱变成秩序——哪怕是在家撸着猫、穿着拖鞋干的。

评论 0