webpack5之HMR原理探究

本文深入探讨了webpack5的模块热替换(HMR)原理,包括其工作流程、源码调试、工作原理分析和实际应用。通过HMR,开发者可以在不丢失应用程序状态的情况下实时更新CSS/JS,显著提升开发效率。文章还详细介绍了启动HMR服务、配置修改、源码调试步骤及关键源码模块的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概念介绍

模块热替换(hot module replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新所有类型的模块,而无需完全刷新。

主要是通过以下几种方式,来显著加快开发速度:

  • 保留在完全重新加载页面期间丢失的应用程序状态。
  • 只更新变更内容,以节省宝贵的开发时间。
  • 在源代码中 CSS/JS 产生修改时,会立刻在浏览器中进行更新,这几乎相当于在浏览器devtools直接更改样式。

注意:HMR只能被应用到开发环境中。

本文示例代码运行环境:
node:12.20.1、webpack:5.37.1、webpack-cli:4.7.0、webpack-dev-server:3.11.2。

二、使用方式

  1. 更新webpack.config.js配置。
+ devServer: {
   
  + port: 9000,
  + host: '127.0.0.1',
  + hot: true,
+ },
plugins: [
  // 定义了devServer.hot:true后,可以省略不写,详细原因可以查看本文四、源码分析 1.1 -> webpack-dev-server/lib/utils/addEntries.js 的代码
  // new webpack.HotModuleReplacementPlugin()
] 
  1. package.json添加scripts。

webpack-cli提供了三个启动webpack-dev-server命令:webpack serve、webpack s、webpack server,它们的作用是相同的。详细原因可以查看本文四、源码分析 -> webpack-cli/lib/webpack-cli.js的代码。

"scripts": {
   
  + "dev": "webpack serve",
}
  1. 在index.js中新增module.hot?.accept方法。
function render() {
   
  root.innerHTML = require('./print.js')
}
render()

+ module.hot?.accept(['./print.js'], render)
  1. 修改print.js。
 function printMe() {
   
  - console.log('Updating print.js');
  + console.log('Updating print.js 1');
 }

 module.exports = printMe

三、源码调试

  1. package.json中增加scripts。
"scripts": {
   
  + "debug": "node ./node_modules/.bin/webpack serve"
}
  1. vscode中打开debug面板,新建一个launch.json配置,启动debug。
{
   
  "version": "0.2.0",
  "configurations": [
    {
   
      "type": "node",
      "request": "launch",
      "name": "debug",
      "runtimeExecutable": "npm",
      "runtimeArgs": [
        "run-script",
        "debug"
      ],
      "skipFiles": [
        "<node_internals>/**"
      ],
      // cwd设置为需要调试项目的根目录
      "cwd": "${workspaceFolder}/webpack/hmr/test"
    }
  ]
}

四、源码分析

以下内容主要是以执行webpack serve命令后,各个模块(文件)主要做了什么事情,来展开阐述。

webpack serve
-> ./node_modules/./bin/webpack中runCli (line 48)

const runCli = cli => {
   
  const pkg = require('webpack-cli/package.json');
	require('webpack-cli/bin/cli.js');
};

-> webpack-cli/bin/cli.js (line 25)

  runCLI(process.argv);

-> webpack-cli/lib/bootstrap.js (line 4)

  const cli = new WebpackCLI();
  await cli.run(args);

-> webpack-cli/lib/webpack-cli.js (line 783)

  const externalBuiltInCommandsInfo = [
    {
   
      name: 'serve [entries...]',
      alias: ['server', 's'],
      pkg: '@webpack-cli/serve',
    }]
  const loadCommandByName = async (commandName) => {
   
    require(pkg);
  }
  this.webpack = require('webpack');
  // line 1834
  createCompiler(options, callback) {
   
    let compiler = this.webpack(options}
    return compiler;
  }

-> @webpack-cli/serve/index.js (line 6)

  const startDevServer_1 = __importDefault(require("./startDevServer"));
  // line 81
  const compiler = await cli.createCompiler();
  await startDevServer_1.default(compiler);

-> @webpack-cli/serve/startDevServer.js (line 92)

  Server = require('webpack-dev-server/lib/Server');
  const server = new Server(compiler, options);
  server.listen(options.port, options.host, (error) => {
   
      if (error) {
   
          throw error;
      }
  });

-> webpack-dev-server/lib/Server.js (line 92)

  // 修改entry和plugins line 72
  updateCompiler(this.compiler, this.options);
  // 监听compiler.hooks.done line 182
  const {
    done } = compiler.hooks;
  done.tap('webpack-dev-server', (stats) => {
   
    this._sendStats(this.sockets, this.getStats(stats));
    this._stats = stats;
  });
  // 实例化express line 168
  this.app = new express();
  // 调用webpack-dev-middleware line 207
  webpackDevMiddleware(
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值