React上手记:一个后端老狗的前端初体验

后端修仙人
2025-12-27 19:07
阅读 1742

上周五晚上十点半,我正蹲在工位上改一个高并发订单超时取消的逻辑,突然钉钉“叮”了一声——产品经理发来消息:“哥,我们新项目要搞个管理后台,用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

最热最新
暂无评论
后端修仙人Lv.1
0
影响力
0
文章
0
粉丝