为什么说“只会写页面的前端,永远成不了高级工程师“?
最近在code review时发现一个有意思的现象:
同样是实现一个仪表盘页面,初级开发者交付了一个完美运行的 DashboardPage.tsx
,而高级开发者却拆出了十几个文件。PM说:"这不是过度设计吗?"
但半年后,当需求疯狂迭代时,真相浮出水面。
初级开发者写的代码成了"屎山",每次改动都牵一发而动全身;而高级开发者的代码,新人接手一周就能上手迭代。
这背后的本质差异,不是技术能力,而是思维模式的代际鸿沟。
残酷真相:你可能一直在用错误的方式写代码
我见过太多工作3-5年的前端,依然停留在"接需求-写页面-提交代码"的循环里。
他们的日常是这样的:
产品说要个用户列表页 → 写个
UserListPage.tsx
说要个报表页 → 再写个
ReportPage.tsx
两个页面都要表格? → 复制粘贴,改改字段名
看起来很高效对吧?
但这种"页面驱动开发"就像在流水线上拧螺丝——你永远在重复劳动,却没有建立任何可复用的资产。
更要命的是:当项目变大,你会发现自己挖的坑自己都填不完。
初级 vs 高级:不只是技术,更是认知维度
很多人以为高级前端就是"Vue/React用得溜"、"性能优化牛逼"、"算法题刷得多"。
错了。
真正的分水岭在于:你是在交付功能,还是在构建系统?
我整理了一个对照表,看看你在哪个层级:
维度 | 初级开发者(页面思维) | 高级开发者(平台思维) |
---|---|---|
关注点 | "这个需求怎么实现?" | "这类需求怎么系统化解决?" |
代码组织 | 按页面拆分组件 | 按职责分层架构 |
复用策略 | 需要时才提取公共组件 | 默认设计可复用模块 |
变更应对 | 改一处牵连多处 | 改动局限在单一层级 |
协作模式 | "我的代码能跑就行" | "团队任何人都能快速接手" |
技术决策 | 选最熟悉的方案 | 评估长期维护成本 |
看到差距了吗?
初级开发者优化的是"个人生产力",高级开发者优化的是"团队战斗力"。
前者解决的是当下,后者设计的是未来。
平台思维的核心:分层架构不是过度设计
很多人一听"分层架构"就觉得是大厂才玩得起的东西,小项目搞这个是"过度设计"。
这是最大的误区。
分层的本质不是为了炫技,而是为了让变化可控。
看看高级开发者是怎么组织代码的:
src/
├── components/ # UI组件层(纯视觉)
│ ├── Button/
│ ├── Table/
│ └── Card/
├── features/ # 特性组件层(业务领域)
│ ├── UserList/
│ ├── InvoiceForm/
│ └── Dashboard/
├── hooks/ # 状态编排层
│ ├── useUserData.ts
│ ├── useAuth.ts
│ └── useTable.ts
├── services/ # 服务层(API通信)
│ ├── api/
│ ├── analytics/
│ └── storage/
└── domain/ # 领域逻辑层(业务规则)├── user/├── invoice/└── validators/
为什么这么拆?
因为每一层对应不同类型的变更:
UI要改风格? → 只动
components/
接口字段变了? → 只改
services/
业务规则调整? → 只碰
domain/
产品要加新功能? → 在
features/
里组装现有能力
这就是平台思维的威力:变更可预测,影响可控制。
初级开发者写的代码,改一个loading状态可能要动三个文件;高级开发者的代码,加一个新页面只需要组装现有模块。
案例拆解:设置页面的两种写法
❌ 初级开发者版本
// SettingsPage.tsx (一个文件800行)
function SettingsPage() {const [user, setUser] = useState(null);const [loading, setLoading] = useState(false);const [activeTab, setActiveTab] = useState('profile');useEffect(() => {// 直接在组件里fetch数据fetch('/api/user/settings').then(res => res.json()).then(data => setUser(data));}, []);const handleSave = async (formData) => {setLoading(true);// 保存逻辑直接写在这里await fetch('/api/user/settings', {method: 'POST',body: JSON.stringify(formData)});setLoading(false);};return (<div>{/* 所有UI、逻辑、状态都堆在一起 */}<Tabs value={activeTab} onChange={setActiveTab}><Tab label="个人信息" /><Tab label="通知设置" /></Tabs>{activeTab === 'profile' && (<form onSubmit={handleSave}>{/* 300行表单代码 */}</form>)}{/* ... */}</div>);
}
问题显而易见:
数据获取、状态管理、UI渲染全部耦合
表单验证逻辑散落在各处
换一个页面要用类似功能? 复制粘贴改改
新人接手要看完800行才能改一个按钮文案
✅ 高级开发者版本
// 1. 服务层 - services/settings.ts
export const settingsService = {getSettings: () => api.get('/user/settings'),updateSettings: (data) => api.post('/user/settings', data)
};// 2. 状态层 - hooks/useUserSettings.ts
export function useUserSettings() {return useQuery({queryKey: ['userSettings'],queryFn: settingsService.getSettings});
}// 3. 表单逻辑层 - hooks/useSettingsForm.ts
export function useSettingsForm() {const { mutate, isLoading } = useMutation({mutationFn: settingsService.updateSettings});return {onSubmit: mutate,isSubmitting: isLoading};
}// 4. 页面组装 - pages/SettingsPage.tsx (只有50行)
export function SettingsPage() {return (<SettingsLayout><SettingsTabs><TabPanel value="profile"><UserProfileForm /></TabPanel><TabPanel value="notifications"><NotificationPreferences /></TabPanel></SettingsTabs></SettingsLayout>);
}// 5. 特性组件 - features/UserProfileForm.tsx
function UserProfileForm() {const { data: settings } = useUserSettings();const { onSubmit, isSubmitting } = useSettingsForm();return (<Form onSubmit={onSubmit} loading={isSubmitting}>{/* 只关注表单UI和交互 */}</Form>);
}
看出区别了吗?
可测试性:每个hook、每个service都能独立测试
可复用性:
useUserSettings
可以在任何页面调用可维护性:改接口只动
services/
,改UI只动组件可扩展性:新增一个tab? 加一个
<TabPanel>
就行
这才是平台代码的样子。
高级开发者的5个"默认习惯"
经过多年观察,我发现真正的高级前端都有这些"肌肉记忆":
1. 组件设计先考虑"插槽"
// ❌ 写死所有逻辑
function DataTable({ data }) {return <table>{/* 固定的渲染逻辑 */}</table>;
}// ✅ 预留扩展点
function DataTable({ data, renderRow, // 自定义行渲染renderEmpty, // 自定义空状态actions // 可插拔的操作按钮
}) {return (<table>{data.length === 0 ? renderEmpty?.() : (data.map(row => renderRow?.(row) ?? <DefaultRow />))}</table>);
}
2. 状态管理自带"边界意识"
// ❌ 全局状态一把梭
const globalStore = {user: {},dashboardData: {},reportData: {},// ... 100个字段
};// ✅ 按领域拆分状态
const userStore = createSlice({ /* 只管用户相关 */ });
const dashboardStore = createSlice({ /* 只管仪表盘 */ });
// 每个store职责单一,互不干扰
3. API调用永远有"容错机制"
// ❌ 裸调接口
const data = await fetch('/api/data').then(r => r.json());// ✅ 统一的错误处理和重试
const { data, error, retry } = useQuery({queryFn: () => api.getData(),retry: 3,onError: (err) => toast.error(err.message)
});
4. 命名不为"准确",只为"共识"
// ❌ 只有自己懂的命名
function getData() { /* ... */ }
const tmp = useMemo(...);// ✅ 团队一眼能懂的命名
function fetchUserDashboardMetrics() { /* ... */ }
const memoizedFilteredUsers = useMemo(...);
5. 每次提交都问:"半年后的自己能看懂吗?"
// ❌ 写完就忘的代码
const x = data.filter(d => d.status === 1 && d.type === 'A');// ✅ 自解释的代码
const STATUS_ACTIVE = 1;
const TYPE_ADMIN = 'A';
const activeAdminUsers = data.filter(user => user.status === STATUS_ACTIVE && user.type === TYPE_ADMIN
);
为什么很多人卡在"初级"起不来?
根本原因:缺少反馈闭环。
很多开发者写完代码就扔给测试,从不回头看:
你写的代码别人维护起来爽不爽?
类似需求第二次来,你是不是又重写了一遍?
半年后的自己,看着现在的代码骂娘了吗?
高级开发者的成长路径是:
写代码 → 被自己的烂代码坑 → 反思总结
重构 → 建立新的设计模式 → 验证效果
在新项目中应用 → 继续迭代优化
这个循环走得越快,成长越快。
从明天开始,这样练习平台思维
实战练习1:重构一个老页面
找一个你半年前写的页面,用平台思维重构:
找出可复用的部分(表格、表单、列表等)
提取数据获取逻辑到独立的hook
把API调用封装到service层
让组件只关心UI渲染
对比重构前后的代码行数和文件数,你会惊讶地发现:看起来"变复杂"了,但维护成本暴降。
实战练习2:设计一个"可配置"的组件
不要写死任何逻辑,全部通过props或context注入:
<DataGridcolumns={columnConfig} // 配置列dataSource={useUserData} // 可替换的数据源rowActions={[editAction, deleteAction]} // 可插拔的操作emptyState={<CustomEmpty />} // 自定义空状态
/>
目标:让这个组件能适配80%的表格需求,而不是每次都写新的。
实战练习3:给团队做一次code review
用平台思维的标准review同事的PR:
这段逻辑能复用吗?
这个状态管理边界清晰吗?
半年后新人能看懂吗?
如果产品改需求,哪些地方要动?
不是为了挑刺,而是建立团队的"质量共识"。
最后:平台思维不是"高大上",是"活下去"
很多人觉得平台思维是大厂的奢侈品,小公司小项目用不上。
恰恰相反。
小团队资源更紧张,人员流动更频繁,历史包袱更重——更需要平台化的代码来对抗混乱。
我见过太多创业公司,因为前期"快速迭代"欠下的技术债,在业务增长后彻底崩盘,不得不推倒重写。
而那些从一开始就用平台思维写代码的团队,即使只有3个前端,也能撑起复杂的产品矩阵。
所以,下次写代码时,多问自己一句:
"我是在交付这个需求,还是在构建一个系统?"
这一念之差,决定了你是流水线工人,还是架构师。
从页面思维到平台思维,不是技术的跃升,是认知的觉醒。
P.S. 如果你发现自己的代码:
每次改需求都要动N个文件
复制粘贴已成习惯
新人看你的代码一脸懵逼
那说明,是时候升级思维模型了。
把这篇文章转给你的前端同事,看看他们在哪个段位? 欢迎评论区聊聊你踩过的坑和突破经验。