从零搭起一个前端项目,我踩过的坑比你刷的题还多
去年十月,我还在一家小公司打着一份“前端+后端+运维”的杂活,老板说要做一个新产品,让我“搞个现代一点的前端”。我当时心里一万个问号:现代一点?是用 Vue3 还是 React18?要不要上 TypeScript?要不要微前端?能不能不写 CSS?(开玩笑的)
但说真的,作为一个双非学校自学编程的大二学生,平时靠刷 LeetCode 和看开源项目混日子,突然要独立从零搭建一个完整的前端项目,内心还是有点慌。尤其最近一边在准备跳槽,一边还在研究 Rust(别问,问就是觉得 unsafe 很酷),时间本来就紧张,结果还得边工作边“造轮子”。
不过,也正是这次经历,让我对“现代化前端项目”到底该长什么样,有了点自己的理解。这篇文章就聊聊我的实战过程,包括架构设计、工具选型、踩坑记录,以及那些让我在深夜加班时差点把键盘砸了的瞬间。
为啥要“从零开始”?
其实一开始我想直接用 Vite + React + Ant Design 起个模板就完事了,毕竟时间紧、任务重,产品经理上周五下班前才改完需求文档,还附带一句“下周三上线哈,加油!”。但转念一想——这不正好是个面试题挑战的好机会吗?
最近投简历时,不少大厂面试官都问:“你有没有从零搭建过项目?怎么组织代码结构?如何做性能优化?”如果我只会 npx create-react-app,那回答起来就太苍白了。于是,我决定认真对待这个“内部产品”,把它当成一个展示工程能力的 demo。
架构设计:不是越新越好,而是越稳越好
很多人一听到“现代化”,就想到 Svelte、Qwik、SolidJS……这些新框架确实香,但现实是:我们团队只有我一个前端,后端是 Java 老哥,测试是兼职的实习生,运维靠宝塔面板。在这种环境下,稳定性 > 新颖性。
所以我定了几个原则:
- 技术栈要主流:方便招人(虽然现在没人可招)、方便查文档、方便甩锅(bushi)。
- 开发体验要快:Vite 必须上,HMR 秒级刷新,谁用谁知道。
- 类型安全不能少:TypeScript 是底线,哪怕业务逻辑简单,也要有类型兜底。
- 可维护性优先:目录结构清晰,组件解耦,避免“上帝文件”。
最终选型如下:
- 框架:React 18(函数式 + Hooks)
- 构建工具:Vite 5
- 状态管理:Zustand(轻量,比 Redux 简单太多)
- UI 库:Ant Design(老板喜欢“企业风”)
- 路由:React Router v6
- 请求库:Axios + 自定义拦截器
- 代码规范:ESLint + Prettier + Husky + lint-staged
- 测试:Vitest + React Testing Library(写了 30% 覆盖率,别打我)
目录结构:别再把所有组件塞进 components/ 了!
以前我写项目,目录结构大概是这样的:
src/
├── components/
│ ├── Button.js
│ ├── Modal.js
│ └── Header.js
├── pages/
└── utils/
结果项目一复杂,components/ 就变成垃圾堆,连自己都找不到哪个组件是干啥的。这次我学乖了,采用 分层 + 按功能聚合 的方式:
src/
├── app/ # 应用入口、路由、全局状态
├── features/ # 业务功能模块(核心!)
│ ├── user/
│ │ ├── components/ # 仅 user 模块内用的组件
│ │ ├── hooks/ # 自定义 hook
│ │ ├── services/ # API 调用
│ │ └── types.ts # 类型定义
│ └── dashboard/
├── shared/ # 全局共享内容
│ ├── components/ # 全局通用组件(如 Layout、Button)
│ ├── hooks/ # 全局 hook(如 useDebounce)
│ └── lib/ # 工具函数、第三方封装
├── assets/ # 静态资源
└── main.tsx
这种结构的好处是:高内聚、低耦合。每个 feature 模块自包含,删掉整个文件夹都不会影响其他功能。而且面试时拿出来讲,HR 都能听懂(夸张了)。
关键配置:Vite + TS + ESLint 的“三位一体”
很多教程只教你怎么跑起来,但没教你怎么“跑得稳”。我花了整整一天调配置,差点被 .eslintrc.cjs 逼疯。
1. Vite 配置(vite.config.ts)
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@features': path.resolve(__dirname, 'src/features'),
'@shared': path.resolve(__dirname, 'src/shared'),
},
},
server: {
port: 3000,
open: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
build: {
outDir: 'dist',
sourcemap: true,
},
});
别小看
alias,它能让你告别../../../地狱。另外,proxy 配置必须加,否则本地开发跨域到哭。
2. ESLint + Prettier 统一风格
最怕团队里有人写分号,有人不写;有人用双引号,有人用单引号。我直接套用 @typescript-eslint/recommended,再加一条规则:
{
"rules": {
"semi": ["error", "always"],
"quotes": ["error", "single"],
"@typescript-eslint/no-unused-vars": "warn"
}
}
配合 lint-staged,每次 commit 前自动 fix,保证主干代码干净。虽然第一次配的时候被 husky 折腾了两小时,但后来发现——真香!
性能优化:别等用户骂你才动手
上线前两天,我用 Lighthouse 跑了一下,Performance 只有 45 分。当时真的想砸电脑。问题出在哪儿?
- 首屏加载慢:所有代码打包成一个 chunk,首页还要加载 dashboard 的图表库。
- 图片未压缩:产品经理给的 Banner 图 5MB,直接拖垮加载速度。
- 重复请求:同一个用户信息,页面里三个地方都在调接口。
解决方案
- 代码分割:React.lazy + Suspense,按路由拆包。
const Dashboard = lazy(() => import('@/features/dashboard/DashboardPage'));
<Route
path="/dashboard"
element={
<Suspense fallback={<Spin />}>
<Dashboard />
</Suspense>
}
/>
- 图片优化:用
sharp写了个脚本,把所有图片转成 WebP,体积减少 70%。 - 请求缓存:用 Zustand 存用户信息,首次请求后后续直接读 state,避免重复拉取。
优化后,Lighthouse Performance 提升到 89,老板看了直呼“专业”。
开发心得:工具链再强,也得看人
这次项目让我深刻体会到:现代化前端 ≠ 一堆新工具堆砌,而是流程、规范、协作的综合体现。
比如,我们虽然用了 TypeScript,但一开始没写 interface,结果传参时老是 any,最后不得不花半天重构。再比如,没加单元测试,上线后发现日期格式化组件在 Safari 上炸了(因为用了 new Date("2023-10-01"),Safari 不认),只能紧急 hotfix。
还有一次,我在调试一个状态更新 bug,死活找不到原因,最后发现是 useEffect 依赖项漏了,导致闭包陷阱。那一刻,我默默打开了 React 官方文档,把“Rules of Hooks”又读了一遍。
面试题挑战:这些问题,我都能答了
通过这个项目,我总结了几道高频面试题的答案,分享给你:
| 面试题 | 我的回答 |
|---|---|
| 如何组织大型前端项目? | 采用 feature-based 结构,每个模块自治,通过 shared 层共享通用逻辑。 |
| 如何做性能优化? | 代码分割、懒加载、图片优化、请求缓存、避免不必要的重渲染(React.memo)。 |
| 为什么选 Zustand 而不是 Redux? | Zustand 更轻量,无模板代码,支持 TypeScript,且不需要 Provider 包裹。 |
| 如何保证代码质量? | ESLint + Prettier + Husky + 单元测试,CI 中加入 lint 和 test 步骤。 |
最后:别怕从零开始
作为一个双非学生,我一度觉得自己写的代码“不够格”。但这次从零搭建项目,让我明白:工程能力不是天生的,是在一次次踩坑、重构、上线中练出来的。
现在这个项目已经稳定运行三个月,虽然用户不多,但至少没崩过(除了那次 Safari 日期 bug)。而我,也靠着这份“完整项目经验”,拿到了两个不错的面试机会。
如果你也在自学,或者正准备跳槽,别只盯着算法题。试着从零做一个真正的产品,哪怕只是个 TodoList,但把它当成上线项目来对待——加类型、写测试、做优化、写文档。你会发现,面试官问的“项目经验”,其实就藏在这些细节里。
对了,最近我在研究 Rust 写 WASM 前端组件,感觉挺酷。下次要是用 Rust 写个前端工具,再来分享!
作者:某双非大学大二学生,白天打工,晚上刷题,周末研究 Rust。
如果你觉得这篇教程有用,欢迎 star 我的 GitHub(虽然现在只有 3 个仓库);
如果你觉得写得烂,也欢迎提 issue —— 毕竟,我还在路上。

评论 0