从零开始构建一个现代化前端项目:裸辞半年后,我重新捡起键盘的血泪经验
去年十月,我做了一个让全家都惊掉下巴的决定——裸辞了。在某大厂干了三年多,经历了无数次“双11压测崩盘”、“凌晨三点回滚上线”和“产品经理说这个需求很简单”的洗礼后,我终于扛不住了。Gap这半年,表面是躺平看世界,实则内心焦虑得半夜爬起来刷 LeetCode,生怕被技术浪潮冲走。
上周五晚上十一点,我坐在上海租的小单间里(离前司就两公里,省下的通勤时间全拿来写代码了),盯着 VS Code 的空白窗口发呆。突然想:既然要重新找工作,不如亲手搭一个能放进简历里的现代前端项目?不为别的,就为了证明自己还没被时代淘汰。
于是,就有了这篇踩坑无数、重构三次、差点又想裸辞(二次)的实战记录。
起手式:别再用 create-react-app 了,它已经“老”了
说实话,刚开搞时我下意识敲了 npx create-react-app my-awesome-app —— 毕竟在大厂那会儿,所有内部脚手架都是基于 CRA 魔改的,闭着眼都能配。但跑起来一看,webpack 4 + Babel 7,连 Top-Level Await 都不支持,ESLint 规则还是三年前的配置。我当场裂开。
现在都 2024 年了,Vite 已经成了事实标准。速度快到离谱,HMR 冷启动不到 300ms,改一行代码秒级刷新,体验直接拉满。于是我删掉 node_modules(对,我又删了一次),重新开整:
npm create vite@latest my-modern-app -- --template react-ts
选 TypeScript 是被逼的。之前在大厂维护一个 JS 项目,光类型推断就让新人 debug 两天。现在跳槽面试,不会 TS 根本进不了二面。而且说实话,写多了反而觉得爽——IDE 自动补全强到飞起,再也不用翻半天文档查 API 返回结构了。
工具链:不是越多越好,而是“刚刚好”
很多教程一上来就塞你一嘴工具:Prettier、ESLint、Husky、lint-staged、Commitizen、Changesets……听着高大上,但实际开发中,90% 的时间都在配这些玩意儿,业务代码一行没写。
我的原则很朴素:只加解决实际问题的工具。
比如 ESLint,我直接用了 eslint-config-react-app + typescript-eslint,再配上 Prettier 做格式化。两者通过 eslint-config-prettier 解耦,互不打架。.eslintrc.js 长这样:
module.exports = {
extends: [
'react-app',
'react-app/jest',
'@typescript-eslint/recommended',
'prettier'
],
plugins: ['@typescript-eslint'],
rules: {
// 禁止 any,但允许我偷懒用一次(调试时)
'@typescript-eslint/no-explicit-any': 'off',
// 强制组件命名规范,避免出现 MyComponent123
'react/function-component-definition': [
'error',
{ namedComponents: 'function-declaration' }
]
}
};
再说 Git hooks。以前在公司,每次 commit 都要跑 2 分钟 lint + test,烦死了。现在我只用 lint-staged 在 staged 文件上跑检查,速度飞快:
// package.json
{
"lint-staged": {
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"]
}
}
提交前自动 fix 可修复问题,格式乱了?不存在的。代码可读性这块,我拿捏得死死的。
代码结构:别让“utils”变成垃圾场
在大厂那会儿,我们有个 src/utils 目录,里面塞了 87 个文件,从日期处理到加密解密再到 mock 数据,啥都有。新人根本不敢动,怕删了某个“关键函数”导致线上炸掉。
这次我给自己立了规矩:按功能域组织,而不是按文件类型。
src/
├── features/
│ ├── auth/ # 认证相关:登录、注册、token 管理
│ ├── dashboard/ # 仪表盘:图表、数据卡片
│ └── profile/ # 用户资料编辑
├── shared/
│ ├── ui/ # 通用组件:Button, Modal, FormItem
│ ├── lib/ # 纯工具函数,无副作用
│ └── hooks/ # 自定义 hooks
└── app/
├── routes.tsx # 路由配置
└── layout.tsx # 全局布局
这样做的好处是:当我要改“用户头像上传”功能时,所有相关代码都在 profile 目录下,不用跨三个文件夹找逻辑。团队协作时也清晰——谁负责哪个 feature 一目了然。
React 写法:告别 class,拥抱函数式与状态管理
曾经我也写过 class 组件,setState 套 setState,回调地狱看得眼花。现在?Function Component + Hooks 是唯一真理。
但光用 useState 和 useEffect 不够。复杂状态(比如表单联动、多步骤向导)很容易写出意大利面条代码。我试过 Redux Toolkit,但感觉有点重;Zustand 上手快,但缺乏 devtools 支持。
最后选了 Jotai —— 一个原子化的状态库。用起来像 useState,但支持异步、派生状态、持久化,还自带 React DevTools 插件。
举个例子,用户登录状态:
// atoms/authAtom.ts
import { atom } from 'jotai';
import { User } from '@/types';
const userAtom = atom<User | null>(null);
const isLoggedInAtom = atom((get) => !!get(userAtom));
export { userAtom, isLoggedInAtom };
在组件里直接用:
const LoginButton = () => {
const [user, setUser] = useAtom(userAtom);
const isLoggedIn = useAtomValue(isLoggedInAtom);
const handleLogin = async () => {
const userData = await api.login();
setUser(userData); // 自动触发 re-render
};
return isLoggedIn ? <Logout /> : <button onClick={handleLogin}>登录</button>;
};
没有 Provider 嵌套,没有 action type 字符串,状态流清晰得像小溪。而且因为是原子粒度,性能优化天然做好了——只 re-render 依赖该 atom 的组件。
性能与体验:别让用户等得想卸载
上周测试时,我把 build 后的包扔 Lighthouse 一跑,Performance 才 68 分。首屏加载 3.2s,JS bundle 2.1MB。我当场血压飙升——这要是放我们前司,测试组早就提 bug 单了:“页面白屏太久,用户流失率上升”。
优化三板斧:
- 代码分割:路由级拆包,用
React.lazy+Suspense - 图片懒加载:
<img loading="lazy" />加上decoding="async" - 移除未用依赖:用
source-map-explorer扫包,发现不小心引入了整个 Lodash,换成lodash-es按需导入
最骚的是,我发现一个第三方 UI 库偷偷打包了 moment.js(170KB!)。换成 date-fns 后,bundle 直接瘦了 15%。
| 优化项 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| Bundle Size | 2.1 MB | 1.3 MB | -38% |
| FCP (首内容绘制) | 2.8s | 1.1s | +60% |
| TTI (可交互时间) | 3.2s | 1.4s | +56% |
现在 Lighthouse Performance 92 分,老板(哦不,是我自己)看了直呼内行。
开发心得:现代化 ≠ 复杂化
折腾这一周,最大的感悟是:所谓“现代化”,不是堆砌最新技术,而是用合适的工具解决真实问题。
在大厂时,我们曾为了“技术先进性”强行上微前端,结果构建流程复杂到 CI 经常超时,本地调试要启五个服务。后来领导一句话点醒我:“用户只关心页面能不能点,不关心你用了 Webpack 还是 Vite。”
所以这次,我没上 Nx,没搞 Monorepo,没写 Cypress E2E(暂时)。一切以快速迭代、清晰维护为目标。代码可读性优先于 clever code,稳定交付优先于炫技。
最后:重启职业生涯的底气
今天凌晨两点,我把项目部署到 Vercel,打开手机扫码预览——丝滑的交互动画、清晰的错误提示、秒开的首屏。那一刻,Gap 半年的焦虑仿佛被清空了。
我知道,重新找工作不会一帆风顺。但至少,当我面对面试官问“你怎么理解现代前端工程化”时,我不再只能背八股文,而是能掏出这个亲手打磨的项目,指着每一行配置说:“这里,是我踩过的坑;这里,是我思考后的选择。”
如果你也在裸辞、转行、或只是想写点干净的代码——别怕从零开始。键盘还在,屏幕还亮,我们就还没输。
(PS:项目已开源,README 里写了详细搭建步骤。欢迎 star,更欢迎 issue 吐槽——毕竟,程序员的快乐,就是互相帮对方 debug 啊。)

评论 0