java热更新原理_webpack热更新(HMR)工作原理

a194d9e553368afd90aa4d2a7eac8743.png

开发环境下的热更新是 webpack 的一个特色功能,其大大提高了开发者的工作效率 -- 我们修改代码的同时浏览器就可以执行更新操作,且会保留当前状态。本文将讲解 webpack 的热更新原理。

提示

  • 建议读者尽量关注热更新的流程,并辅以源码加以理解。下面对于源码的分析只会列出主干部分的内容,具体细节及边界处理感兴趣可自行查阅。
  • 另外,webpack 的源码随着版本的更新改动比较大,文末参考资料的第一篇文章是去年十二月份写的,和现在(2020-12)对比有很多代码都发生了巨大的变化,再早一点的教程也是变化很大。但是总体的热更新流程万变不离其宗。

本文的 webpack 配置如下所示:

webpack.config.js

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

module.exports = {
    
  entry: {
    
    app: "./src/index.js",
  },
  output: {
    
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new HtmlWebpackPlugin({
    
      title: 'Hot Module Replacement'
    })
  ],
  devtool: 'source-map',
  devServer: {
    
    hot: true
  }
}

index.js

import printMe from './print.js';

function component() {
    
  const element = document.createElement('div');
  const btn = document.createElement('button');

  element.innerHTML = "hello world!";

  btn.innerHTML = 'Click me and check the console!';
  btn.onclick = printMe;

  element.appendChild(btn);

  return element;
}

let element = component(); // 当 print.js 改变导致页面重新渲染时,重新获取渲染的元素
document.body.appendChild(element);

if (module.hot) {
    
  module.hot.accept('./print.js', function () {
    
    console.log('Accepting the updated printMe module!');
    document.body.removeChild(element);
    element = component(); // 重新渲染页面后,component 更新 click 事件处理
    document.body.appendChild(element);
  });
}

print.js

export default function printMe() {
    
  console.log('hello world');
}

直观感受

当我们修改 print.js 的内容时,我们能够看到的是控制台打印的内容更改了,说明热替换一切正常,这很明显。但是我们还可以看看 调试工具 network 选项卡的一些信息,在这里我们可以推测出一个大致的流程。

让我们启动项目,页面初次刷新,我们得到了 app.bundle.js 的入口js文件,值得注意的是,浏览器还和开发服务器保持了一个 websocket 连接,根据传输的信息我们可以肯定这和热更新密切相关。

31c8fc211986122a205151466508cfb2.png

现在我们修改 print.js 的内容,然后等待热更新加载。

再次抓包查看,发现更新了三部分内容:

  • 一个json文件

f42d57658676340be11221137afbf776.png
  • 一个js文件

从这个 js 里面我们可以找到我们修改的内容,可以猜出这个文件里面的内容将把旧代码替换。

同时,观察代码发现,返回的内容是一个函数,可知这个js文件可以在适当地位置被执行, 这种通过函数通信的方式也称为 JSONP

cfb64472eca523dd61a910c57a95e1a6.png
  • websocket 连接内容的变化

我们可以看到 websocket 的信息也添加了一些内容,而且第一次生成的 hash 值刚好是新请求到的 js 文件的 hash 值。新生成的 hash 值应该是下一次热更新的文件。

c926c7192768689f38722baf2fd03ad2.png

我们不难得出这样的一个大致的流程:

  • 热更新通过浏览器和 webpack 开发服务器的 websocket 实现通信。
  • 当我们修改文件时,webpack 开发服务器会通过 websocket 通知浏览器需要更新。
  • 浏览器知晓需要更新时,利用之前的 hash 值,通过 ajax 请求获取新的 js 文件,然后执行一系列业务逻辑,让新的代码覆盖旧的代码。

接下来我们结合源码,深入分析整个流程。

本地代码监听与编译

我们不难想到,开发环境下一定有一个监听模块,用来监听本地文件的更新情况。这个功能由 webpack-dev-middleware 来实现,来看源码,只列出主干部分。

// webpack-dev-middleware/index.js
module.exports = function wdm(compiler, opts) {
    
  const context = createContext(compiler, options);
  // 开始监听
  context.watching = compiler.watch(options.watchOptions, (err) => {
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值