webpack热更新

79 阅读3分钟

模块热替换

定义

模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,替换、添加或删除模块,而无需重新加载整个页面。

  • 保留应用程序状态
  • 只更新变更内容
  • 源码中修改css/js时,会立刻在浏览器中更新

[扩展]module、chunk和boundle的区别

  • module: 每个通过import,require,@import引入的文件都是一个module
  • chunk: 用来管理构建过程的中间量,是由一个或多个module组成,一般情况下,分为入口chunk(entryChunk)和子chunk(split chunk)和import动态加载模块都是一个chunk
  • boundle: 打包生成的文件,一般来说一个chunk匹配一个boundle,主要取决于具体的配置

模块API

if (module.hot) {

  module.hot.accept('./library.js', function() {

    // 对更新过的 library 模块做些事情...

  });

}

如果启用了热更新,则它的接口将被暴露在module.hot

  1. accept,接受accept给定的依赖模块的更新,并触发一个回调函数来响应更新,除此之外,你可以附加一个可选的error处理程序
   module.hot.accept(

      dependencies, // 可以是一个字符串或字符串数组

      callback // 用于在模块更新后触发的函数

      errorHandler // (err, {moduleId, dependencyId}) => {}

   ); 
  1. check: 测试所有加载的模块以进行更新,如果有更新则应用他们
  module.hot

    .check(autoApply)

    .then((outdatedModules) => {

      // 超时的模块...

    })

    .catch((error) => {

      // 捕获错误

    });

效果展示

  1. 热更新基础效果展示【见代码】

http://localhost:8080/·

  1. 说明

    1. 当文件修改的时候,在network中我们可以看到两个请求

      1. 第一个是hash.hot-update.json,返回一个新的hash和change的chunkId
      2. 第二个是chunk.hash.hot-update.js文件,以jsonp的形式返回一个方法调用,第一个参数是chunkId,第二个参数是一个对象,以键值队的形式返回被更新的文件也就是moduleId和它更新后的模块内容
      3. 执行回调函数,模块更新

工作原理

  1. 热更新原理展示【配合代码和下图】
  2. 代码(见下篇)

webpack实现热更新效果的时候主要有上图几个方面协同作用

服务端:

  1. 如图创建webpack实例
  2. 创建server服务器,并建立websocket连接
  3. 添加done事件回调
  4. compiler.watch监听到文件发生变化,进行重新编译
  5. 编译完成之后触发done钩子,websocket向客户端(即)浏览器端发送hash和ok事件

客户端(浏览器端)

  1. 连接websocket

  2. 热模块方法为module添加parent、children、hot和accept()和check()方法

  3. websocket监听事件

  4. websocket监听到hash和ok事件

    1. hash事件中,将hash保存为currentHash
    2. ok事件中,会触发webpackHotUpdate事件

热更新模块(合并HotModuleReplacement.runtime和webpack/hot/dev-server文件)

webpack/hot/dev-server文件

  1. 监听到webpackHotUpdate事件

  2. 执行hotCheck方法

    1. 发送lashHash.hot-update.json请求,告知新的hash和被更改的chunk
    2. 即{"h":"99a3d7248ecb0dbf2410","c":{"main":true}}
  3. 拿到被更改的chunk后执行hotDownloadUpdateChunk方法

    1. 使用jsonp方式发送请求chunkId.lastHash.hot-update.js
    2. 返回更新后的模块内容,并用webpackHotUpdate方法包裹,该方法可以直接执行
  4. 移除旧的模块,从父模块找到该模块的accept接收的回调方法

  5. 执行回调方法

  6. 更新完成