我在字节搬砖两年后,终于鼓起勇气回头学 React
上周五晚上九点半,我盯着屏幕上 Go 服务返回的 JSON 数据发呆。我们组刚把一个内部配置中心重构完,后端用 Go 写得飞快,但前端还是老掉牙的 jQuery + Bootstrap 组合。产品经理小王又在群里艾特我:“后端接口都 Ready 了,前端啥时候能上线?双 11 前必须搞定啊!”
我叹了口气——作为一个在字节基础架构组搬了快两年砖的后端工程师,整天和 etcd、Kubernetes、Go 协程打交道,但面对前端这块“神秘领域”,说实话一直有点发怵。直到上个月团建喝多了,隔壁前端组的老李拍着我肩膀说:“兄弟,现在不懂点 React,连看日志界面都显得你 out 了。”
行吧,被逼上梁山。趁着周末两天没值班,我决定从零开始搞个 React 项目,目标很朴素:把我们那个丑到爆的内部工具页面重写一下。没想到这一写,居然真跑起来了!今天就把我踩过的坑、装过的 VSCode 插件、以及怎么把 Go 后端和 React 前端串起来的经验,全盘托出。
为什么是 React?而不是 Vue 或 Svelte?
其实我一开始想试试 Vue3,毕竟听说上手快。但打开 GitHub 一搜,公司内部 80% 的新项目(包括抖音中台某些模块)都用 React,连我们组最近孵化的 DevOps 平台也选了它。再加上 TypeScript + React 的组合在字节几乎成了标配,面试官张哥上次还吐槽:“现在招人,不会 React hooks 直接 pass。”
所以,与其对抗趋势,不如拥抱它。而且我发现 React 的组件化思想,和我们写 Go 微服务的“单一职责”原则莫名契合——每个组件只干一件事,props 就像 API 入参,state 就像服务内部状态。这种思维迁移让我这个后端仔舒服了不少。
环境搭建:别被 node_modules 劝退
第一步永远是最劝退的。我在家里的 MacBook 上折腾,先确保 Node.js 装了(建议用 nvm 管理版本),然后:
npx create-react-app config-center-ui --template typescript
小贴士:一定要加
--template typescript!别信什么“先学 JS 再转 TS”的鬼话,在字节,没有类型检查的代码根本过不了 CI。
等了五分钟(网速慢+依赖多),项目骨架搭好了。这时候我的 VSCode 自动弹出提示:“检测到 React 项目,是否安装推荐扩展?”——赶紧点“是”。我常用的插件包括:
- ESLint:配合 Prettier,自动格式化,拯救我的缩进强迫症
- Auto Rename Tag:改 HTML 标签再也不怕漏关
- Thunder Client:轻量级 API 测试,比 Postman 快
- Import Cost:实时显示 import 包的大小,防 bundle 膨胀
进入项目目录,npm start,localhost:3000 弹出熟悉的欢迎页。那一刻,我仿佛看到了双 11 上线的曙光(虽然可能只是幻觉)。
第一个组件:从展示静态数据开始
我们的内部工具很简单:拉取一个配置列表,展示 key-value 对。先不管后端,我 mock 一段数据:
// src/components/ConfigList.tsx
import React from 'react';
interface ConfigItem {
key: string;
value: string;
}
const mockConfigs: ConfigItem[] = [
{ key: "feature_flag_new_ui", value: "true" },
{ key: "timeout_ms", value: "5000" },
];
export const ConfigList: React.FC = () => {
return (
<div className="config-list">
<h2>Configuration Center</h2>
<ul>
{mockConfigs.map((item, index) => (
<li key={index}>
<strong>{item.key}</strong>: {item.value}
</li>
))}
</ul>
</div>
);
};
注意几个细节:
- 用了 TypeScript 定义
ConfigItem接口,避免后期字段乱改导致运行时崩 key={index}虽然不完美,但对静态 mock 数据够用(真实场景应该用唯一 ID)- 没有 CSS-in-JS,直接用原生 className,因为后续要接入公司统一 Design Token
在 App.tsx 里引入:
import { ConfigList } from './components/ConfigList';
function App() {
return (
<div className="App">
<ConfigList />
</div>
);
}
刷新页面,列表出来了!虽然丑,但至少不是白屏。那一刻我差点截图发朋友圈:“React 初体验成功!”(后来忍住了,怕被前端同事笑)
连接 Go 后端:跨域、fetch 和错误处理
现在要把 mock 数据换成真实接口。我们的 Go 服务跑在 http://localhost:8080/api/configs,返回标准 JSON。
但在 React 里直接 fetch 会遇到 CORS 问题。本地开发时,最简单的解法是在 package.json 里加代理:
{
"name": "config-center-ui",
"proxy": "http://localhost:8080"
}
这样所有 /api 开头的请求都会被转发到 Go 服务。重启 dev server,然后改组件:
import React, { useState, useEffect } from 'react';
interface ConfigItem {
key: string;
value: string;
}
export const ConfigList: React.FC = () => {
const [configs, setConfigs] = useState<ConfigItem[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
fetch('/api/configs')
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
return res.json();
})
.then(data => {
setConfigs(data);
setLoading(false);
})
.catch(err => {
console.error('Failed to fetch configs:', err);
setError(err.message);
setLoading(false);
});
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<div className="config-list">
<h2>Configuration Center</h2>
<ul>
{configs.map((item, index) => (
<li key={item.key}>
<strong>{item.key}</strong>: {item.value}
</li>
))}
</ul>
</div>
);
};
这里用了 useEffect 做副作用(相当于 componentDidMount),配合 useState 管理加载状态。关键点:
- 一定要处理 error 和 loading 状态,否则用户看到白屏会以为系统挂了
- Go 后端记得设置 CORS 头(生产环境用 Nginx 配),否则线上还是会跨域失败
调试时,我一度卡在 404——原来 Go 路由写的是 /configs,但我前端写了 /api/configs。查了半天才发现代理只转发路径,不改路由。改完 Go handler 后,数据终于出来了!
性能优化:别让 render 成为瓶颈
作为性能敏感型后端,我本能地打开了 Chrome DevTools 的 Performance 面板。发现每次 state 更新,整个列表都在 re-render。虽然现在只有两条数据,但以后上千条呢?
于是加上 React.memo 防止子组件不必要更新:
const ConfigItemRow: React.FC<{ item: ConfigItem }> = React.memo(({ item }) => {
return (
<li>
<strong>{item.key}</strong>: {item.value}
</li>
);
});
// 在 ConfigList 里用 ConfigItemRow 替代 inline li
另外,fetch 放在 useEffect 里没问题,但如果未来要支持搜索或分页,就得考虑用 useCallback 包裹回调函数,避免闭包陷阱。这些细节,在字节的 Code Review 里都是必查项。
部署上线:GitHub Pages + CI/CD
本地跑通只是开始。为了演示给小王看,我决定部署到 GitHub Pages。
先安装 gh-pages:
npm install --save-dev gh-pages
在 package.json 加脚本:
{
"scripts": {
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
},
"homepage": "https://yourname.github.io/config-center-ui"
}
然后 npm run deploy,几分钟后,页面就在 https://yourname.github.io/config-center-ui 可访问了!
当然,公司内部肯定不用 GitHub Pages,而是走自研的 CDN + 容器平台。但原理一样:build 出静态文件,扔到对象存储,配个域名。值得一提的是,我们组最近用 Go 写了个 CLI 工具,一键打包前端并推送到内部 Registry,比手动操作快十倍。
总结:后端学前端,值不值?
折腾完这个小项目,我最大的感受是:React 没想象中那么可怕。它的核心思想很清晰——UI = f(state),剩下的都是工程细节。
更重要的是,当我能独立完成一个前后端闭环,和前端同事沟通时底气足多了。上周站会上,我甚至能指出他们某个组件的状态管理可以优化,老李惊讶地说:“你什么时候偷偷卷 React 了?”
如果你也是后端,别被“前端水深”吓住。从一个小项目开始,用你熟悉的工程思维去拆解,很快就能上手。毕竟,在字节这样的公司,全栈能力越来越吃香——就算不做 fullstack,懂一点前端,debug 时也能少求人一次。
对了,这个项目的代码我放 GitHub 上了:github.com/yourname/config-center-ui(名字已脱敏)。欢迎 star,更欢迎提 PR 教我怎么写得更好。
最后,祝各位搬砖人少加班,多上线。双 11,我们稳了!

评论 0