React上手记:一个后端老狗的前端初体验
上周五晚上十点半,我正蹲在工位上改一个高并发订单超时取消的逻辑,突然钉钉“叮”了一声——产品经理发来消息:“哥,我们新项目要搞个管理后台,用React吧,你不是会点前端吗?”
我差点一口老血喷在键盘上。
我一个写了四年美团外卖Java服务的后端,日常打交道的是Redis集群、Kafka削峰、分布式事务……什么时候成了“会前端”的人了?但转念一想,最近组里确实在推全栈能力,加上成都这边生活节奏舒服,下班早(相对北上广),不如趁机学点新东西,万一哪天想跳槽呢?
于是,抱着“死马当活马医”的心态,我开始了React的入门之路。没想到这一路,坑比火锅底料还多。
起手式:环境配置就给我整不会了
按理说,装个React不就是npx create-react-app my-app完事?但现实总是骨感的。
我本地Node.js版本是14.15(别问,问就是公司项目卡死的版本),结果执行命令时直接报错:
You are running Node 14.15.0.
Create React App requires Node 16 or higher.
好家伙,连门都没进就被拦住了。我赶紧升级Node到18,结果本地其他老项目跑不起来了。纠结半天,最后祭出了nvm大法,切换版本来回折腾,终于把脚手架搭起来了。
开发心得:前端工具链更新速度堪比外卖骑手抢单——快得离谱。建议新手直接用最新LTS版Node,别像我一样被历史包袱拖住。
启动项目后,默认页面是个旋转的React logo,配色清新,动画丝滑。但当我试图改一行字保存时,浏览器居然没热更新!控制台疯狂刷红:
Fast Refresh had to perform a full reload...
查了半天,原来是我在WSL2里开发,文件系统监听有问题。解决方案?要么用原生Windows终端,要么加个环境变量:
CHOKIDAR_USEPOLLING=true
写进.env文件,重启,搞定。那一刻我真想给Chokidar作者寄一箱郫县豆瓣酱——感谢你让我又学了个新词。
写第一个组件:状态管理把我绕晕了
产品需求很简单:做个用户列表页,点“加载更多”按钮追加10条数据。
我信心满满地写:
function UserList() {
let users = [];
const loadMore = () => {
users = [...users, ...fetchNewUsers()];
};
return (
<div>
{users.map(u => <div key={u.id}>{u.name}</div>)}
<button onClick={loadMore}>加载更多</button>
</div>
);
}
结果点击按钮,页面纹丝不动。
我:???
调试半小时才发现——React根本不认你直接改变量!必须用useState声明状态,用setXXX触发重渲染。这和我熟悉的Vue双向绑定完全是两个世界。
正确姿势:
import { useState } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
const loadMore = async () => {
const newUsers = await fetchNewUsers(); // 模拟API调用
setUsers(prev => [...prev, ...newUsers]); // 关键:用函数式更新
};
return (
<div>
{users.map(u => <div key={u.id}>{u.name}</div>)}
<button onClick={loadMore}>加载更多</button>
</div>
);
}
踩坑总结:React的状态是“不可变”的(immutable),任何修改都必须通过
setState。这设计虽然啰嗦,但能避免很多副作用问题——尤其在高并发场景下,这种确定性反而成了优势。作为后端,我居然有点欣赏这种“严谨”。
异步请求:别在渲染函数里发请求!
产品那边催得紧,我赶紧接真实API。一开始图省事,直接在组件顶部调用:
// 错误示范!
const userData = fetch('/api/users').then(res => res.json());
结果页面一打开,控制台刷屏10+次请求。React严格模式(Strict Mode)会在开发环境故意双渲染组件,用来暴露副作用问题。我的代码正好踩雷。
正确的做法是用useEffect:
useEffect(() => {
const fetchData = async () => {
const data = await fetch('/api/users').then(res => res.json());
setUsers(data);
};
fetchData();
}, []); // 空依赖数组 = 只在组件挂载时执行一次
但这里又有个坑:如果用户快速切换页面,请求还没回来就卸载组件,这时候再setUsers会报warning:
Can't perform a React state update on an unmounted component.
解决方案?加个isMounted标志,或者用AbortController取消请求。我选了后者:
useEffect(() => {
const controller = new AbortController();
const fetchData = async () => {
try {
const res = await fetch('/api/users', { signal: controller.signal });
const data = await res.json();
setUsers(data);
} catch (e) {
if (e.name !== 'AbortError') console.error(e);
}
};
fetchData();
return () => controller.abort(); // 组件卸载时取消请求
}, []);
资源提醒:网络请求、定时器、WebSocket连接——这些外部资源一定要在
useEffect的清理函数里释放!否则内存泄漏分分钟教你做人。
样式与用户体验:别让用户干等
产品小王看了demo后幽幽地说:“能不能加个loading?现在点按钮没反应,用户以为卡了。”
我:……行吧。
于是加上isLoading状态:
const [loading, setLoading] = useState(false);
const loadMore = async () => {
setLoading(true);
try {
const newUsers = await fetchNewUsers();
setUsers(prev => [...prev, ...newUsers]);
} finally {
setLoading(false);
}
};
按钮文字动态切换:
<button onClick={loadMore} disabled={loading}>
{loading ? '加载中...' : '加载更多'}
</button>
顺便给列表加个骨架屏(Skeleton),提升感知性能。虽然只是个内部管理后台,但用户体验无小事——这可是我在美团做C端产品时学到的铁律。哪怕是一个按钮的反馈,都可能影响骑手/商家的操作效率。
构建与部署:生产环境又给我上了一课
本地跑得好好的,一npm run build,生成的静态文件丢到Nginx上,刷新页面直接404。
原因?React Router用的是客户端路由(如/users/list),但Nginx不知道这是前端路由,以为是真实路径,自然找不到文件。
解决方案:配置Nginx,所有非静态资源请求 fallback 到index.html:
location / {
try_files $uri $uri/ /index.html;
}
另外,记得检查package.json里的"homepage"字段。如果部署在子路径(如https://admin.example.com/react-app/),必须设置:
{
"homepage": "/react-app/"
}
否则静态资源路径会404。这个坑我踩过两次——第一次是去年双11前夜,第二次就是这次。运维同事看我的眼神已经从“同情”变成“习以为常”了。
总结:代码人生,贵在折腾
从抗拒到接受,再到能独立完成一个功能模块,我花了一周时间(主要是晚上和周末)。虽然React的声明式思维、Hooks机制一开始让我这个命令式编程老手很不适应,但越用越觉得香。
尤其是组件化思想——把UI拆成一个个可复用的小块,和我们后端微服务的理念异曲同工。一个Button组件,可以传不同props实现Primary/Secondary/Danger样式;就像我们的OrderService,通过不同参数处理普通单、预约单、闪购单。
| 对比项 | 后端(Java/Spring) | 前端(React) |
|---|---|---|
| 核心单元 | Service/Controller | Component/ Hook |
| 状态管理 | Redis/DB | useState/useReducer |
| 异步处理 | CompletableFuture | useEffect + async |
| 通信方式 | HTTP/gRPC | Props/Context |
当然,前端的生态碎片化确实让人头疼。光是打包工具就有Webpack、Vite、Rollup……而我们后端,Maven + Spring Boot 足矣。但换个角度想,这种活跃度也意味着创新快、选择多。
最后几句心里话
写这篇文章,不是为了教大家React(官方文档比谁都清楚),而是想分享一个后端视角的前端初体验。技术没有高低贵贱,只有适用场景。在美团,我们常说“以客户为中心”,其实对开发者也一样——你的“客户”可能是产品经理、测试同学,甚至是未来的自己。
当你写的代码能让别人少加班、少背锅、少掉头发,那这就是有价值的产品思维。
至于React?它不过是我工具箱里的新扳手。明天可能换成Vue,后天可能是Svelte。但解决问题的能力,才是程序员真正的护城河。
对了,产品小王刚又发消息:“哥,下个需求要加图表,ECharts还是D3?”
我回了个微笑表情,默默打开了Ant Design Charts文档……
代码人生,就是不断打怪升级。坑踩多了,路就平了。

评论 0