Webpack HMR 和 React-Hot-Loader

  • Webpack 提供了热替换(Hot-Module-Replacement,简写为 HMR)的接口,使得我们的代码更新时不需要刷新页面就能够应用更新后的代码。
  • React-Hot-Loader 是 Webpack 的第三方 loader,它针对 React 应用提供了更强大的热替换功能。
    这两者是什么关系,实际开发中要如何选择呢?

Webpack HMR

HMR 是 Webpack 的主要功能之一,它不依赖特定的框架。HMR 的工作流程如下:

  • 在 Webpack config 文件中引入 webpack.HotModuleReplacementPlugin 插件,开启 dev server 的热替换功能:
    57c1e74c-a68d-40fb-8e30-baf133888307
  • 在业务代码中使用 module.hot.accept("./someFileName", callbackToRunWhenThatFileIsRecompiled) API 来注册模块更新回调
  • Webpack dev server 会监听代码更新并且重新编译
  • Webpack dev server 通过 websocket 将更新后的代码推送到浏览器端
  • 通过 module.hot.accept 注册的回调被执行

在回调函数里做什么是由开发者决定的,通常来说在回调里会重引入更新后的模块并替换现有模块:
xxx
对 React 来说,在 HMR 里做的是重新引入 root component,然后重新渲染。因为 HMR 是对 root component 的热替换,所以替换之后 root component 和它内部的 component 的 state 都会丢失,但是对于保存在像 Redux store 等外部数据容器中的状态则可以保持:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';'

import './index.css';

const rootEl = document.getElementById("root");

ReactDOM.render(
    <App />,
    rootEl
);

if(module.hot) {
    // 当 App 组件或者它依赖的模块更新时,重新引入更新后的模块并重新渲染
    module.hot.accept("./App", () => {
        const App = require("./App").default;
    
        ReactDOM.render(
            <App />,
            rootEl
        );
    });
}

render();

通过 Webpack HMR 实现热替换不依赖于 React-Hot-Loader,它是 Webpack 自身的功能。

React-Hot-Loader

React-Hot-Loader 使用了 Webpack HMR API,针对 React 框架实现了对单个 component 的热替换,并且能够保持组件的 state。
React-Hot-Loader 在编译时会在每一个 React component 外封装一层,每一个这样的封装都会注册自己的 module.hot.accept 回调,它们会监听每一个 component 的更新,在当前 component 代码更新时只替换自己的模块,而不是整个替换 root component。
同时,React-Hot-Loader 对 component 的封装还会代理 component 的 state,所以当 component 替换之后依然能够保持之前的 state。
尽管 React-Hot-Loader 功能强大,但因为实际情况中会有很多边界情况,会导致奇怪的问题。

总的来说,Webpack HMR 在 component 更新后不会保持 component 的 state,但它的实现和配置更简单,更可靠。通过 HMR 和 Redux 等数据管理工具 一起使用,将应用状态保存在组件外,基本能够实现和 React-Hot-Loader 类似的效果。

知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。