热模块替换 (HMR) 是一种在应用程序中更新模块的技术,无需重新加载页面。它为开发者提供了极佳的体验,并且 React Router 在使用 Vite 时支持它。
HMR 会尽力在更新过程中保留浏览器状态。例如,假设您在模态框内有一个表单,并且您填写了所有字段。一旦您保存对代码的任何更改,传统的实时重新加载将强制刷新页面,导致所有这些字段被重置。每次您进行更改时,您都必须再次打开模态框并再次填写表单。
但是,使用 HMR,所有这些状态都在更新之间得以保留。
React 已经拥有通过其虚拟 DOM响应用户交互(例如单击按钮)更新 DOM 的机制。如果 React 也可以处理响应代码更改更新 DOM,那不是很好吗?
这正是React 快速刷新的意义所在!当然,React 都是关于组件的,而不是一般的 JavaScript 代码,因此 React 快速刷新只处理导出 React 组件的热更新。
但是 React 快速刷新确实有一些限制,您应该了解。
React 快速刷新不会保留类组件的状态。这包括内部返回类的更高阶组件。
export class ComponentA extends Component {} // ❌
export const ComponentB = HOC(ComponentC); // ❌ Won't work if HOC returns a class component
export function ComponentD() {} // ✅
export const ComponentE = () => {}; // ✅
export default function ComponentF() {} // ✅
为了让 React 快速刷新跟踪更改,函数组件必须命名,而不能是匿名的。
export default () => {}; // ❌
export default function () {} // ❌
const ComponentA = () => {};
export default ComponentA; // ✅
export default function ComponentB() {} // ✅
React 快速刷新只能处理组件导出。虽然 React Router 为您管理路由导出,例如 action
、headers
、links
、loader
和 meta
,但任何用户定义的导出都将导致完全重新加载。
// These exports are handled by the React Router Vite plugin
// to be HMR-compatible
export const meta = { title: "Home" }; // ✅
export const links = [
{ rel: "stylesheet", href: "style.css" },
]; // ✅
// These exports are removed by the React Router Vite plugin
// so they never affect HMR
export const headers = { "Cache-Control": "max-age=3600" }; // ✅
export const loader = async () => {}; // ✅
export const action = async () => {}; // ✅
// This is not a route module export, nor a component export,
// so it will cause a full reload for this route
export const myValue = "some value"; // ❌
export default function Route() {} // ✅
👆 路由可能不应该导出这样的随机值。如果您想在路由之间重用值,请将它们放在自己的非路由模块中。
export const myValue = "some value";
当向组件添加或删除 Hook 时,React 快速刷新无法跟踪组件的更改,导致仅在下一次渲染时进行完全重新加载。在 Hook 更新后,更改应该再次导致热更新。例如,如果您向组件添加 useState
,您可能会在下一次渲染时丢失该组件的本地状态。
此外,如果您正在解构 Hook 的返回值,如果解构的键被移除或重命名,React 快速刷新将无法保留组件的状态。例如
export default function Component({ loaderData }) {
const { pet } = useMyCustomHook();
return (
<div>
<input />
<p>My dog's name is {pet.name}!</p>
</div>
);
}
如果您将键 pet
更改为 dog
export default function Component() {
- const { pet } = useMyCustomHook();
+ const { dog } = useMyCustomHook();
return (
<div>
<input />
- <p>My dog's name is {pet.name}!</p>
+ <p>My dog's name is {dog.name}!</p>
</div>
);
}
那么 React 快速刷新将无法保留状态 <input />
❌。
在某些情况下,React 无法区分现有组件的更改和新组件的添加。React 需要 key
来消除这些歧义并在修改兄弟元素时跟踪更改。