主题
医院排班与医院上线(Day15 3/3)
本页对应 React + TypeScript + 医通项目,共 6 段视频,约 2 小时 38 分。
学习目标
- 能够执行
npm run build生成build产物,说明交付后端的目录内容,并在模拟 Express 服务中完成静态资源目录、history fallback、接口代理三项配置,验证页面刷新与登录请求正常。 - 能够使用
i18next与react-i18next创建中英文语言包和初始化配置,在组件中通过useTranslation的t函数渲染文案,并使用changeLanguage完成语言切换。 - 能够集成 antd 国际化语言包,使用
ConfigProvider的locale控制 Table 分页等组件内置文案,并与自定义国际化状态联动切换。 - 能够解释 React diff 的同层比较与最小量更新原则,对比列表
key使用唯一 id 与索引的差异,分析向前或中间追加删除时的错误复用与输入框串位问题,并给出正确key选择规则。 - 能够应用 React 核心复习点完成代码说明或实现,包括函数/类组件的 state、props、事件对象与 this、生命周期与
useEffect、受控/非受控组件、ref、路由参数及 Redux Toolkit 基本读写流程。 - 能够结合入行前三个月、半年市场复盘、学历与技术积累、目标大厂等阶段,制定个人 3–5 年职业发展计划,并列出各阶段可执行动作与检验节点。
知识点
本节说明
本节笔记标题为“医院排班”,但转写稿实际围绕项目上线部署、React 国际化、antd 国际化、diff 算法与 key、React 复习及职业规划展开。下面按转写稿整理,不补造医院排班业务细节。
打包上线部署:交给后端的是 build 目录
为什么需要:开发目录里有 src、public、node_modules 等,后端并不懂前端工程,不能直接把源码目录丢给后端。
怎么想:前端先完成构建,把可部署的静态资源目录交给后端;后端再按服务器类型部署。
怎么做:执行 npm run build,得到 build 目录,里面通常有 index.html、压缩后的 JS/CSS。可以用 Express 模拟服务器:初始化 package.json、安装 express,配置静态资源目录、history fallback 和代理。
bash
npm run build
npm init -y
npm i express
npm i http-proxy-middleware
npm i connect-history-api-fallbackjs
const express = require('express');
const app = express();
app.use(express.static(__dirname));
// 404 时回退到 index.html,解决刷新前端路由 404
app.use(require('connect-history-api-fallback')());
app.listen(5000);易错点:public/index.html 里资源路径最好用 / 开头,表示最终 build 根目录;用 ./ 容易随地址变化找错目录。部署时要解决三件事:history 重定向、静态资源目录、代理跨域。刷新后白屏,本质是服务器按前端路由路径找文件找不到,不是 React 路由本身坏了。
i18n 国际化:语言包 + T 函数 + 切换语言
为什么需要:页面文字不能写死,否则中英文切换要改所有组件。
怎么想:把文案按组件放到语言包 JSON 中,组件里用 t('hello') 读当前语言的值;切换语言时换语言包。
怎么做:安装 i18next、react-i18next,在 src/locales 下建 en-US.json、zh-CN.json,再建 i18n.ts 初始化。组件里用 useTranslation() 取 t 和 i18n,用 i18n.changeLanguage('en') 或 'zh' 切换。
js
const { t, i18n } = useTranslation();
// 显示
t('hello');
// 切换
i18n.changeLanguage('en');易错点:语言包字段名要对应;默认语言在初始化配置里指定;切换语言后组件内仍用同一个 t 读不同语言包。转写稿提到 XSS 与 escapeValue,实际配置需以 i18next 文档为准,不要机械照搬。
antd 国际化:ConfigProvider 管组件内部文案
为什么需要:Table 分页、快速跳转等文案在 antd 组件内部,不受自己写的 t 函数控制。
怎么想:自己写的文字用 i18n;antd 组件内部文字用 antd 内置语言包。
怎么做:安装 antd,从 antd/locale/zh_CN、antd/locale/en_US 导入语言包,用 ConfigProvider 包裹区域并设置 locale。
jsx
<ConfigProvider locale={flag ? zhCN : enUS}>
<Table />
</ConfigProvider>易错点:只切 i18n 不会自动切 antd 分页文案;locale 要跟随同一个状态切换。
diff 算法与 key:列表 key 优先用唯一 ID
为什么需要:setState 后 React 会比对新旧虚拟 DOM,尽量复用真实 DOM,实现最小量更新。
怎么想:同层比较;标签不同直接重建;组件不同也直接重建。列表比较靠 key 判断是否同一节点。
怎么做:列表项优先用唯一 ID:key={item.id}。只有纯展示、不涉及顺序变化时,才可退而用索引。
易错点:向前追加、删除、中间插入时用索引会导致错误复用,出现输入框内容串位;key 重复会报错。医院编码这类业务唯一值可以作唯一标识,但业务校验仍要单独做。
React 复习与职业规划要点
为什么需要:上线部署、国际化、diff 都是项目收尾能力,面试和入职还会追问基础。
怎么想:把知识按“状态、props、事件、生命周期、路由、仓库、受控/非受控”归类复习。
怎么做:类组件关注 this 指向、生命周期;函数组件关注 useState、useEffect;受控组件通过状态和 onChange 获取值,非受控通过 ref 取真实 DOM 值。
易错点:函数组件没有 this 指向问题;useEffect 返回函数对应卸载清理;不要把所有知识混成一团。职业规划部分转写稿强调心态、前三个月坚持、定期面试了解行情和学历规划,属于经验建议,不是技术结论。
先明确交付物:给后端的是 build 目录
为什么需要:项目写完不能把源码目录直接丢给后端。后端通常不熟悉前端工程,拿到 src、public、node_modules 这类目录也不知道怎么运行。
怎么想:前端开发时用 webpack 等工具启动开发服务器;上线时要先构建,把源码编译、压缩成浏览器可直接加载的静态文件。
怎么做:在项目根目录执行:
bash
npm run build构建完成后会生成 build 目录。打开可以看到 index.html,里面引用了打包后的 JS、CSS。最终交付给后端的是这个 build 目录,而不是源码目录。
易错点:不要给后端 src、public、node_modules;不要以为 npm run dev 能直接上线。构建产物才是交付物。
用 Express 模拟服务器部署
为什么需要:后端部署方式取决于公司服务器,可能是 Nginx、Apache 等,但核心操作相同。我们可以用 Express 模拟,提前发现上线问题。
怎么想:把 build 目录当成网站根目录,启动一个本地服务器,访问 http://localhost:5000 看页面是否正常。
怎么做:进入 build 目录初始化并安装依赖:
bash
npm init -y
npm i express新建 server.js:
js
const express = require('express');
const app = express();
app.listen(5000, () => {
console.log('server run at 5000');
});运行:
bash
node server.js易错点:命令要在 build 目录下执行;端口不要和已有服务冲突;server.js 是模拟文件,不是前端源码的一部分。
配置静态资源目录
为什么需要:访问服务器时,浏览器要能加载 index.html、JS、CSS、图片等静态资源,否则页面空白。
怎么想:Express 需要把当前 build 目录设置为静态资源目录。
怎么做:
js
const express = require('express');
const app = express();
app.use(express.static(__dirname));
app.listen(5000, () => {
console.log('server run at 5000');
});易错点:express.static 的目录要指向 build 根目录;如果路径写错,页面会 404。public/index.html 里资源路径最好用绝对路径 /,不要用 ./,否则刷新或进入子路由后相对路径容易失效。
配置代理解决接口跨域
为什么需要:开发阶段跨域代理由 webpack devServer 的 proxy 完成;打包上线后 webpack 不生效,生产环境请求前缀可能变成 /prod/api,需要服务器单独代理。
怎么想:把以前 devServer 里的代理配置迁移到 Express,用 http-proxy-middleware 做中间件。
怎么做:安装:
bash
npm i http-proxy-middleware在 server.js 中配置:
js
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use('/prod/api', createProxyMiddleware({
target: 'http://后端服务器地址',
changeOrigin: true,
pathRewrite: { '^/prod/api': '' }
}));易错点:target 要换成真实后端地址;pathRewrite 要按实际接口前缀调整;代理前缀必须和生产环境请求前缀一致。调试时看 Network:请求是否 200、地址和参数是否正确。
刷新 404 与 history fallback
为什么需要:前端路由在地址栏刷新时,服务器会按路径找文件,例如 /hospital/hospitalSet,但服务器上没有这个文件,于是 404。
怎么想:访问不存在的路径时,用 index.html 替代 404 响应,让前端路由接管。
怎么做:安装:
bash
npm i connect-history-api-fallback在 server.js 中,必须放在所有中间件最前面:
js
const history = require('connect-history-api-fallback');
app.use(history());完整顺序建议:
js
app.use(history());
app.use('/prod/api', createProxyMiddleware({ /* ... */ }));
app.use(express.static(__dirname));易错点:history() 不放在最前面,可能导致静态资源或代理不生效;刷新 404 不是路由写错,而是服务器找不到对应文件。
上线部署要交代给后端的三件事
为什么需要:后端不一定熟悉前端构建产物,最好明确告诉他 build 目录要处理什么。
怎么想:部署服务器不同,配置方式不同,但操作目标一致。
怎么做:交付 build 目录,并说明:1)配置静态资源目录;2)配置代理服务器;3)配置 404 重定向到 index.html。
易错点:不要只丢一个压缩包不说明;不要假设后端会自动处理 history 路由;代理和重定向顺序要检查。按这三件事排查,页面、接口、刷新问题基本都能定位。
课堂画面






代码示例
代码1:Express 模拟部署 build 目录(JS/Node)
bash
# 把 npm run build 生成的 build 目录交给后端
# 进入 build 目录后执行:
npm init -y
npm i express http-proxy-middleware connect-history-api-fallbackjs
// server.js 放在 build 根目录,与 index.html 同级
const express = require('express');
const history = require('connect-history-api-fallback');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// 1. 所有 404 响应回退到 index.html,解决前端路由刷新 404
// 必须放在所有配置最前面
app.use(history());
// 2. 当前 build 目录作为静态资源目录
app.use(express.static(__dirname));
// 3. 打包后代理要单独配置:/prod/api -> 后端接口地址
app.use(
'/prod/api',
createProxyMiddleware({
target: 'http://localhost:8080', // 换成实际后端接口地址
changeOrigin: true,
pathRewrite: {
'^/prod/api': ''
}
})
);
app.listen(5000, () => {
console.log('server run on 5000');
});要点解释:
- 给后端的不是
src/public/node_modules,而是npm run build生成的build目录。 app.use(history())解决刷新后找不到index.html下子路径的问题,转写稿强调它要放在所有配置最前面。express.static(__dirname)把当前build目录设为静态资源目录。pathRewrite: { '^/prod/api': '' }用于去掉请求前缀,和开发环境proxy配置对应。- 易错点:
index.html里资源路径建议用/开头;target必须替换成真实后端地址;开发环境的 proxy 打包后不生效,需要单独配置。
代码2:React + i18next + antd 国际化(TSX)
bash
npm i i18next react-i18next antdjson
// src/locales/en_US.json
{
"hello": "hello",
"name": "Name",
"age": "Age",
"address": "Address"
}json
// src/locales/zh_CN.json
{
"hello": "你好",
"name": "姓名",
"age": "年龄",
"address": "地址"
}ts
// src/locales/i18n.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import enUS from './en_US.json';
import zhCN from './zh_CN.json';
i18n.use(initReactI18next).init({
resources: {
en: { translation: enUS },
zh: { translation: zhCN }
},
lng: 'en',
interpolation: {
escapeValue: false
}
});
export default i18n;tsx
// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './locales/i18n';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);tsx
// src/App.tsx
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ConfigProvider, Table } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import enUS from 'antd/locale/en_US';
export default function App() {
const { t, i18n } = useTranslation();
const [flag, setFlag] = useState(false);
const [current, setCurrent] = useState(1);
const [pageSize, setPageSize] = useState(3);
const [total] = useState(10);
const columns = [
{ title: t('name'), dataIndex: 'name' },
{ title: t('age'), dataIndex: 'age' },
{ title: t('address'), dataIndex: 'address' }
];
const dataSource = [
{ key: '1', name: '张三', age: 18, address: '上海' },
{ key: '2', name: '李四', age: 20, address: '北京' }
];
const toggleLang = () => {
setFlag(!flag);
i18n.changeLanguage(flag ? 'en' : 'zh');
};
return (
<div>
<h1>{t('hello')}</h1>
<button onClick={toggleLang}>{flag ? '中文' : '英文'}</button>
<ConfigProvider locale={flag ? zhCN : enUS}>
<Table
columns={columns}
dataSource={dataSource}
pagination={{
current,
pageSize,
total,
showQuickJumper: true,
showSizeChanger: true,
pageSizeOptions: [3, 10, 20],
onChange: (page, size) => {
setCurrent(page);
setPageSize(size);
}
}}
/>
</ConfigProvider>
</div>
);
}要点解释:
i18n.ts中resources配置语言包映射,lng是默认语言。- 组件里用
useTranslation()解构出t和i18n,t('hello')读取语言包中的hello字段。 - 切换语言用
i18n.changeLanguage(...),界面中仍然只写t('hello'),不需要改显示代码。 - antd 组件内部文案不走
t,要用ConfigProvider+antd/locale/zh_CN、antd/locale/en_US。 - 易错点:
i18n.ts要在入口文件导入;语言包 key 要和t()参数一致;escapeValue:false是转写稿中的配置点,React 默认会转义文本,不要直接dangerouslySetInnerHTML。
代码3:React diff 算法 key 示例(JSX)
jsx
// App.jsx
import { useState } from 'react';
export default function App() {
const [todos, setTodos] = useState([
{ id: 1, title: '吃饭', isDone: false },
{ id: 2, title: '睡觉', isDone: false }
]);
const addFront = () => {
setTodos([{ id: 99, title: '钱', isDone: false }, ...todos]);
};
const addBack = () => {
setTodos([...todos, { id: 100, title: '后', isDone: false }]);
};
return (
<div>
<button onClick={addFront}>向前追加</button>
<button onClick={addBack}>向后追加</button>
<ul>
{todos.map((todo, index) => (
<li key={index}>
{todo.title}
<input />
</li>
))}
</ul>
</div>
);
}要点解释:
- 上面用
key={index}是为了复现转写稿中的问题:在input里输入内容后点“向前追加”,会出现值串位。 - 原因是 diff 按同级比较,并用标签名 + key 判断复用;索引在向前追加、中间追加、删除时会变化,导致错误复用。
- 修复方式是把
key={index}改成唯一标识:key={todo.id}。 - 易错点:
key不要重复;纯列表展示可用索引;涉及向前追加、中间追加、删除时不要用索引;向后追加用索引影响较小,但仍推荐唯一id。
常见错误
- 现象:把包含
src/public/node_modules的源码目录直接交给后端,后端无法部署。原因:交付物错误,后端需要的是构建产物。修法:本地执行npm run build,只交付build/dist目录,并说明入口是index.html。 - 现象:部署后首页能打开,刷新任意前端路由直接 404。原因:服务端未做 history fallback,按 URL 找不到真实文件。修法:服务端将非静态资源/404 回退到
index.html;Express 中用connect-history-api-fallback,且放在静态资源和代理中间件之前。 - 现象:部署后接口请求打到当前域名
/prod/api/...失败或跨域。原因:开发期 devServer proxy 只在开发环境生效,生产环境未配置代理。修法:生产服务器配置反向代理和路径重写;Express 中用http-proxy-middleware配/prod转发并pathRewrite。 - 现象:静态资源 404、页面白屏或样式丢失。原因:
index.html中资源引用用了相对路径,部署到子路径或刷新后按当前 URL 解析。修法:资源引用改为根路径/开头(按部署基路径配置),并把静态资源目录指向build/dist。 - 现象:自己写的文案能中英切换,antd Table 分页、快速跳转等组件内文案不跟着变。原因:只用了 i18n 的
t,未给 antd 组件注入locale。修法:从antd/locale引入zhCN/enUS,用ConfigProvider locale包裹,并让locale与全局语言状态同步。 - 现象:列表向前插入后,输入框内容串位。原因:
key用了数组索引,diff 错误复用节点。修法:用稳定唯一id作key;索引仅限纯展示、不重排、不插入删除时谨慎使用。 - 现象:受控输入框无法输入或表现为只读。原因:只绑了
value,没绑onChange。修法:补onChange,用最新输入更新对应 state。 - 现象:类组件事件处理函数里
this指向不对。原因:方法未绑定且作为回调传递。修法:构造函数bind、箭头函数定义,或内联箭头调用。
练习
- 一个 React 项目要交付后端部署,请列出构建命令、交付目录,以及后端部署必须处理的三类问题。
- 部署后刷新
/hospital/set出现 404,请写出排查顺序,并说明 Express 中修复中间件应放在什么位置。 - 页面登录请求为
http://localhost:5000/prod/api/admin/login且失败,请说明生产代理要改哪里,pathRewrite应如何处理。 - antd Table 的分页文案不随语言切换,请给出修复方案,并说明需要与哪个全局状态同步。
- 列表用索引作
key,向前插入后 input 内容串位。请说明复用过程错在哪里,并给出修复方案。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 打包上线部署 | 27:36 |
| 2 | i18n国际化 | 22:00 |
| 3 | antd中的国际化 | 11:46 |
| 4 | react-diff算法-key | 39:15 |
| 5 | react复习 | 34:24 |
| 6 | 未来规划 | 23:29 |
小结
- 前端交付物是
build/dist,不是源码目录;交付时要说清入口和静态资源目录。 - 生产部署至少盯三件事:history fallback、静态资源目录、接口代理。
- history fallback 必须尽早生效,否则依赖前端资源加载的配置都可能失效。
- 国际化要分两层处理:业务文案走
t,组件库文案走ConfigProvider locale。 - 列表
key优先用稳定唯一id;索引只在纯展示、尾部追加等安全场景谨慎使用。 - 排错顺序先看 Network:请求地址、参数、响应码,再定位代理、路由和静态资源。
- 上线后“能点不能刷”不是玄学,优先查 fallback 和资源路径。
- 工作前三个月重点不是证明自己多强,而是稳住心态、补齐排错和沟通能力。