从零搭起一个前端项目,我踩过的坑比你刷的题还多

Bean没注入
2026-01-21 14:33
阅读 757

去年十月,我还在一家小公司打着一份“前端+后端+运维”的杂活,老板说要做一个新产品,让我“搞个现代一点的前端”。我当时心里一万个问号:现代一点?是用 Vue3 还是 React18?要不要上 TypeScript?要不要微前端?能不能不写 CSS?(开玩笑的)

但说真的,作为一个双非学校自学编程的大二学生,平时靠刷 LeetCode 和看开源项目混日子,突然要独立从零搭建一个完整的前端项目,内心还是有点慌。尤其最近一边在准备跳槽,一边还在研究 Rust(别问,问就是觉得 unsafe 很酷),时间本来就紧张,结果还得边工作边“造轮子”。

不过,也正是这次经历,让我对“现代化前端项目”到底该长什么样,有了点自己的理解。这篇文章就聊聊我的实战过程,包括架构设计、工具选型、踩坑记录,以及那些让我在深夜加班时差点把键盘砸了的瞬间。


为啥要“从零开始”?

其实一开始我想直接用 Vite + React + Ant Design 起个模板就完事了,毕竟时间紧、任务重,产品经理上周五下班前才改完需求文档,还附带一句“下周三上线哈,加油!”。但转念一想——这不正好是个面试题挑战的好机会吗?

最近投简历时,不少大厂面试官都问:“你有没有从零搭建过项目?怎么组织代码结构?如何做性能优化?”如果我只会 npx create-react-app,那回答起来就太苍白了。于是,我决定认真对待这个“内部产品”,把它当成一个展示工程能力的 demo。


架构设计:不是越新越好,而是越稳越好

很多人一听到“现代化”,就想到 Svelte、Qwik、SolidJS……这些新框架确实香,但现实是:我们团队只有我一个前端,后端是 Java 老哥,测试是兼职的实习生,运维靠宝塔面板。在这种环境下,稳定性 > 新颖性

所以我定了几个原则:

  1. 技术栈要主流:方便招人(虽然现在没人可招)、方便查文档、方便甩锅(bushi)。
  2. 开发体验要快:Vite 必须上,HMR 秒级刷新,谁用谁知道。
  3. 类型安全不能少:TypeScript 是底线,哪怕业务逻辑简单,也要有类型兜底。
  4. 可维护性优先:目录结构清晰,组件解耦,避免“上帝文件”。

最终选型如下:

  • 框架: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 分。当时真的想砸电脑。问题出在哪儿?

  1. 首屏加载慢:所有代码打包成一个 chunk,首页还要加载 dashboard 的图表库。
  2. 图片未压缩:产品经理给的 Banner 图 5MB,直接拖垮加载速度。
  3. 重复请求:同一个用户信息,页面里三个地方都在调接口。

解决方案

  • 代码分割: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

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