从零开始构建一个现代化前端项目:一个老咸鱼的血泪复盘

产品说很简单
2025-12-18 19:22
阅读 867

作者注:我在某二线互联网公司干了三年多前端,Vim 是我的主战场(别问,问就是 :wq 永远比鼠标快),最近在研究性能优化和架构设计。眼看着同龄人要么升职加薪、要么跳槽拿 offer,我也在悄悄更新简历——但在这之前,得先把手头这个新项目整明白。

上周五晚上十点半,我瘫在工位上啃着冷掉的黄焖鸡,盯着屏幕上那个空荡荡的 git init 界面发呆。产品经理小王刚甩过来一个“轻量级”需求:“我们想做一个内部工具平台,支持动态表单、权限控制、数据看板……对了,下周三要给老板演示。”

我差点把鸡骨头吐他脸上——这叫轻量?你管这叫轻量?但没办法,二线厂嘛,人少事多,deadline 像催命符。更糟的是,旧项目用的是 React 16 + Webpack 4 + class 组件全家桶,代码里还混着 jQuery(别问,问就是历史包袱)。要是再这么搞下去,别说跳槽面试被问“你们项目的架构设计思路”,我自己都答不上来。

于是,我咬牙决定:重头来过,搞一套现代化的前端基建。这篇文章,就是我踩坑三天、掉头发无数后总结出来的实战记录——不为别的,就为下次面试能理直气壮地说:“我主导过一个从 0 到 1 的 React 工程化项目。”


起手式:别一上来就 npx create-react-app

很多新人(包括三年前的我)一听到“新项目”,第一反应就是 create-react-app。不是说 CRA 不好,但它就像外卖——方便,但不够“私厨”。我们这次要的是可维护、可扩展、高性能的“私房菜”。

所以第一步,我直接 mkdir modern-frontend && cd $_ && git init,然后掏出我的 Vim 开始手搓配置。

技术栈选型:既要新潮,也得稳

类别 选择 理由
框架 React 18 + TypeScript 团队已有 React 基础,TS 强类型能减少低级 Bug
构建工具 Vite 比 Webpack 快 10 倍不止,HMR 秒级刷新,开发体验起飞
状态管理 Zustand 轻量、无样板代码,比 Redux 简洁太多
UI 库 Ant Design 5 内部系统,需要快速出效果,AntD 的 Table/Form 太香
路由 React Router v6 标配,没得选
代码规范 ESLint + Prettier + Husky 强制统一风格,避免 PR 时扯皮
测试 Vitest + React Testing Library Vite 生态配套,跑测试飞快

吐槽一句:产品经理上次说“UI 随便搞搞就行”,结果看到 AntD 默认主题直接说“太丑了”,非要改成深色模式……行吧,反正 CSS Variables 改起来也就两行。


配置环节:Vite + TS + AntD 的酸爽组合

先装基础依赖:

npm create vite@latest . -- --template react-ts
npm install antd zustand react-router-dom @types/node -D

然后重点来了:按需加载 + 主题定制。AntD 默认是全量引入,包体积爆炸。得配合 unplugin-vue-components 的兄弟——unplugin-antd-iconsvite-plugin-style-import(不过现在 AntD 5 已经原生支持 ES Module,其实不用额外插件了)。

vite.config.ts 关键配置:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { resolve } from 'path'

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src')
    }
  },
  css: {
    // AntD 5 的 CSS-in-JS 支持主题变量
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
        modifyVars: {
          // 自定义主题色
          'primary-color': '#1DA57A',
          'border-radius-base': '4px'
        }
      }
    }
  },
  build: {
    // 分包策略:第三方库单独 chunk
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return 'vendor'
          }
        }
      }
    }
  }
})

搞定之后,启动速度从 Webpack 的 30s+ 直接降到 800ms。我当场在 Slack 里@运维:“以后 CI/CD 能不能快点?你看我本地开发多丝滑!” 运维回我:“你先把 E2E 测试覆盖率提到 80% 再说话。”


架构设计:别让组件变成意大利面条

以前我们项目最大的问题是:所有逻辑塞在 useEffect 里,组件动不动 500 行。这次我强制自己遵守 分层原则

src/
├── app/              # 应用入口 & 路由
├── features/         # 业务功能模块(如 user, form, dashboard)
│   └── form/
│       ├── components/   # 局部组件
│       ├── hooks/        # 自定义 hook
│       ├── services/     # API 调用
│       └── types.ts      # 类型定义
├── shared/           # 全局共享
│   ├── ui/           # 通用 UI 组件(Button, Modal)
│   ├── lib/          # 工具函数
│   └── store/        # 全局状态
└── main.tsx

比如一个动态表单功能,我把表单配置解析、字段渲染、校验逻辑全部抽到 features/form 下。这样即使产品经理明天说“我们要支持 50 种字段类型”,我也只需要改这个目录,不会污染全局。

Zustand 的使用也很克制:只存用户信息、权限、全局 loading 状态。其他局部状态一律用 useState 或自定义 hook。别一上来就把所有东西塞进 store,那是 Redux 时代的陋习。


性能优化:面试官最爱问的“实际案例”

说到性能,很多人只会背“懒加载、代码分割、防抖节流”。但真实场景里的性能问题,往往藏在细节里

场景 1:Table 渲染卡顿

内部系统有个数据看板,表格有 20 列 × 500 行。第一次渲染直接卡死,Chrome DevTools 显示主线程阻塞 3s+。

解决方案

  1. 虚拟滚动:用 react-window 替代原生 Table。
  2. Memoization:所有单元格组件用 React.memo 包裹。
  3. 避免 inline function:把 renderCell={(record) => <MyCell data={record} />} 提到外层定义。
// 错误示范 ❌
<Table columns={[
  { title: 'Name', render: (_, record) => <NameCell record={record} /> }
]} />

// 正确做法 ✅
const nameRender = useCallback((_, record) => <NameCell record={record} />, [])
<Table columns={[{ title: 'Name', render: nameRender }]} />

优化后,FPS 从 12 提升到 58,滚动丝滑如德芙。

场景 2:首屏加载慢

Lighthouse 评分只有 45,主要问题是 JS bundle 太大(2.1MB)。

对策

  • 路由级代码分割React.lazy + Suspense
  • 关键资源预加载:用 <link rel="preload"> 加载首屏 CSS
  • 移除未使用图标:AntD 的 Icon 全量引入很恐怖,改用按需:
// 只导入需要的图标
import { UserOutlined, DashboardOutlined } from '@ant-design/icons'

最终 bundle 降到 680KB,Lighthouse 分数 89。我把报告截图发到团队群,测试同学回:“所以现在点开首页不会白屏三秒了?感谢🙏”


开发体验:Vim 党的倔强与妥协

作为 Vim 党,我 IDE 用得少,但这次还是装了 VS Code —— 主要为了它的 TypeScript 智能提示GitLens。不过 .vimrc 我可没扔:

" 在 Vim 里直接运行 npm script
nnoremap <leader>b :!npm run build<CR>
nnoremap <leader>d :!npm run dev<CR>

调试方面,我重度依赖 React DevTools 的 Highlight Updates 功能。只要看到不该 re-render 的组件闪红框,立马知道哪里漏了 useCallbackuseMemo

另外,控制台日志要打但更要删!我们项目曾因线上残留 console.log 导致内存泄漏(别笑,真事)。现在用 ESLint 规则禁止提交含 console 的代码:

// .eslintrc.js
rules: {
  'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn'
}

面试题素材:这些坑我都替你踩过了

如果你正在准备面试,下面这些点可以直接当“项目亮点”讲:

  • 如何保证大型 React 项目的可维护性?

    我们通过 feature-based 目录结构 + TypeScript 强约束 + 自动化代码规范,确保新人一周内能上手业务模块。

  • 做过哪些性能优化?量化结果是什么?

    首屏加载从 3.2s 优化到 1.1s(Lighthouse 数据),Table 渲染 FPS 从 12 提升至 58,bundle 体积减少 67%。

  • 为什么选 Zustand 而不是 Redux Toolkit?

    内部工具对状态复杂度要求不高,Zustand 无需模板代码、支持异步中间件、且天然兼容 React 18 并发特性。


最后:搞完项目,该跳槽了?

这套现代化基建搭完,我花了一周时间重构旧功能。虽然双 11 前又熬了两个通宵(产品经理临时加了个“导出 PDF”需求,而浏览器打印样式是个黑洞),但整体代码质量肉眼可见地提升。

更重要的是,我现在敢在简历上写“主导前端工程化建设”了。上周猎头推了个一线大厂的岗,JD 里赫然写着:“熟悉 React 性能优化与现代构建工具链”——嘿,这不就是我这几天干的事儿吗?

当然,也可能我只是个二线厂的普通前端,每天在 deadline 和技术理想之间反复横跳。但至少,下一次面对“从零开始”的需求时,我不再手抖了。

开发心得:所谓“现代化”,不是堆砌最新技术,而是用合适的工具解决实际问题。Vite 再快,写不出业务也是白搭;React 18 再香,用户等三秒加载照样骂娘。技术人的价值,在于把复杂留给自己,把简单留给用户——哪怕这个用户,只是隔壁工位那个总改需求的产品经理。

(完)


附:关键命令速查

# 初始化项目
npm create vite@latest . -- --template react-ts

# 安装依赖
npm install antd zustand react-router-dom

# 开启开发服务器
npm run dev

# 构建生产包
npm run build

# 运行 Lighthouse 审计
npx lighthouse http://localhost:3000 --view

评论 0

最热最新
暂无评论
产品说很简单Lv.1
0
影响力
0
文章
0
粉丝