Webpack 模块热替换(HMR)原理与实现

作者:初晴

时间:2026-10-22

浏览:650

本文深入解析 Webpack 的模块热替换(HMR)功能,阐述其作为开发环境工具的原理与配置方法。通过示例展示如何结合 HotModuleReplacementPlugin 与 NamedModulesPlugin 优化模块标识,并详细说明 JavaScript 模块中 module.hot.accept 接口的实现方式。文章还剖析了 webpackHotUpdate 的底层调用逻辑及 hotApply 的实际执行过程,帮助开发者理解无刷新更新背后的机制。

文章目录

HMR 概述与配置

HMR 全称为 Hot Module Replacement(热模块替换),其概念类似于 .NET 中的热插拔技术,旨在程序运行期间动态更新特定模块。该功能主要服务于开发阶段,对生产环境并无直接帮助,这也是它与 .NET 热插拔的主要区别。其核心效果是实现界面的无刷新更新。

HMR 基于 Webpack Dev Server (WDS) 运行。对于样式模块,style-loader 可直接利用此机制实现无刷新更新;然而,JavaScript 模块需要额外的处理逻辑。由于 HMR 仅适用于开发环境,因此需要分别配置生产与开发两套环境。


const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');

const PATHS = {
 app: path.join(__dirname, 'app'),
 build: path.join(__dirname, 'build'),
};

const commonConfig={
 entry: {
  app: PATHS.app,
 },
 output: {
  path: PATHS.build,
  filename: '[name].js',
 },
 plugins: [
  new HtmlWebpackPlugin({
   title: 'Webpack demo',
  }),
 ],
}
 
function developmentConfig(){
 const config ={
  devServer:{
   //使能历史记录api
   historyApiFallback:true,
    hotOnly:true,//关闭热替换 注释掉这行就行
    stats:'errors-only',
   host:process.env.Host,
   port:process.env.PORT,
   overlay:{
    errors:true,
    warnings:true,
   }
  },
   plugins: [
   new webpack.HotModuleReplacementPlugin(),
  ],
 };
  return Object.assign(
  {},
  commonConfig,
  config,
  {
   plugins: commonConfig.plugins.concat(config.plugins),
  }
 );
}

module.exports = function(env){
 console.log("env",env);
 if(env=='development'){
  return developmentConfig();
 }
  return commonConfig;
};

在 webpack.config.js 中,通过 env 参数区分 commonConfig(通用配置)与 developmentConfig(开发配置)。该 env 参数来源于 package.json 中的脚本定义。

HMR 概述与配置

根据上述配置,执行 npm start 启动时进入开发环境配置;若直接执行 build,则采用生产环境配置(即第一节所述的不带 WDS 的 webpack 直接启动方式)。此处使用了 Object.assign 语法合并配置。启动后,控制台会输出两条相关日志。

HMR 概述与配置

此时 HMR 服务已启动。若更新 component.js 文件,

HMR 概述与配置

模块标识优化

日志显示没有模块被热更新。日志中的数字如 39,36 代表模块 ID,这种格式不够直观。可以通过引入特定插件来改善这一显示效果。


plugins: [
   new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
  ],

重新启用服务后,

HMR 概述与配置

JavaScript 模块更新接口

模块名称变得直观易读。但预期的更新仍未触发,因为还需要实现特定的接口。


import component from './component';
let demoComponent=component();
document.body.appendChild(demoComponent);

//HMR 接口
if(module.hot){
  module.hot.accept('./component',()=>{
    const nextComponent=component();
    document.body.replaceChild(nextComponent,demoComponent);
    demoComponent=nextComponent;
  })
}

同时修改 component.js 以适配该接口:


export default function () {
 var element = document.createElement('h1');
 element.innerHTML = 'Hello webpack';
 return element;
}

HMR 概述与配置

此时页面成功更新。每次代码改动后,页面上都会新增一个带有 hot-update.js 字样的文件,结构类似如下:


webpackHotUpdate(0,{

/***/ "./app/component.js":
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony default export */ __webpack_exports__["default"] = function () {
 var element = document.createElement('h1');
 element.innerHTML = 'Hello web ';
 element.className='box';
 return element;
};

/***/ })

})

底层更新机制分析

系统通过 webpackHotUpdate 对相应模块进行更新。其中 0 表示模块 ID,"./app/component.js" 表示模块名称。其结构形式为 webpack(id,{key:function(){}})。函数外包裹括号的作用尚不明确。webpackHotUpdate 的定义如下:


this["webpackHotUpdate"] = 
 function webpackHotUpdateCallback(chunkId, moreModules) { // eslint-disable-line no-unused-vars  
     hotAddUpdateChunk(chunkId, moreModules);
    if(parentHotUpdateCallback) parentHotUpdateCallback(chunkId, moreModules);
  } ;

从结构上看,更新涉及 ID 和对应修改的模块。但实际执行更新的是 hotApply 方法。整个热更新机制较为复杂,效果上类似于 MVVM 的数据绑定。有兴趣者可深入研究。不建议在生产环境中使用 HMR,因为这会增加整体文件体积且对生产构建无益。下一节将介绍样式加载,style-loader 即利用了 HMR。但对于 JS 模块需编写额外代码,这略显繁琐。

示例代码:webpack-ch3_jb51.rar

参考资料:

https://survivejs.com/webpack/developing/configuring-hmr/

https://webpack.js.org/configuration/dev-server/

系列文章:

【webpack】-- 自动刷新与解析

【webpack】-- 入门与解析

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章