热模块替换
本页内容

热模块替换

热模块替换是一种在应用中更新模块而无需重新加载页面的技术。这是一种很棒的开发者体验,React Router 在使用 Vite 时支持它。

HMR 尽力在更新之间保留浏览器状态。例如,假设您在模态框中有一个表单,并且您填写了所有字段。一旦您保存对代码的任何更改,传统的实时重新加载将硬刷新页面,导致所有这些字段都被重置。每次进行更改时,您都必须再次打开模态框并再次填写表单。

但是使用 HMR,所有这些状态都跨更新保留。

React Fast Refresh

React 已经有通过其 虚拟 DOM 响应用户交互(如单击按钮)来更新 DOM 的机制。如果 React 也可以处理响应代码更改来更新 DOM,那岂不是很棒吗?

这正是 React Fast Refresh 的全部内容!当然,React 关注的是组件,而不是通用的 JavaScript 代码,因此 React Fast Refresh 仅处理导出的 React 组件的热更新。

但是 React Fast Refresh 确实有一些您应该注意的限制。

类组件状态

React Fast Refresh 不会为类组件保留状态。这包括内部返回类的高阶组件。

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 Fast Refresh 才能跟踪更改。

export default () => {}; // ❌
export default function () {} // ❌

const ComponentA = () => {};
export default ComponentA; // ✅

export default function ComponentB() {} // ✅

支持的导出

React Fast Refresh 只能处理组件导出。虽然 React Router 为您管理 路由导出,如 action headerslinksloadermeta,但任何用户定义的导出都将导致完全重新加载。

// 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

当 Hook 被添加或从组件中移除时,React Fast Refresh 无法跟踪组件的更改,这会导致仅在下一次渲染时才完全重新加载。在 Hook 更新后,更改应该再次导致热更新。例如,如果您向组件添加 useState,您可能会在下一次渲染时丢失该组件的本地状态。

此外,如果您正在解构 Hook 的返回值,如果解构的键被移除或重命名,React Fast Refresh 将无法为组件保留状态。例如:

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 Fast Refresh 将无法保留状态 <input /> ❌。

组件键

在某些情况下,React 无法区分现有组件被更改和新组件被添加。React 需要 key 来消除这些情况的歧义,并在修改兄弟元素时跟踪更改。

文档和示例 CC 4.0