修改 entry 配置
首先通过启动 webpack-dev-server 会修改 webpack.config.js 的 entry 配置,新增两个入口文件:
- webpack-dev-server/client/index.js
- webpack/hot/dev-server.js
webpack-dev-server/client/index.js 包含的是客户端向服务端通信的相关代码。
webpack/hot/dev-server.js 包含的是客户端进行检查和热更新的相关代码。
启动本地服务
webpack-dev-server 会启动 webpack 创建 compiler 实例,然后在本地启动两个服务:一个 express 静态资源服务,用来接收浏览器发送的资源请求和发送资源;一个 websocket 服务,用来和浏览器进行双向通信。
监听 webpack 编译
监听 webpack 的 done 钩子。当 webpack 编译结束后,会向浏览器发送通知触发 hash 和 ok 事件。
webpack 监听文件变化
使用 webpack-dev-middleware 监听本地文件的变化,如果文件发生了变化就会立刻对文件代码进行编译和打包,然后将编译后的文件写入到内存中。
浏览器接收到热更新通知
在 webpack-dev-server/client/index.js 中包含 hash 和 ok 事件。hash 事件会接收服务端发送过来的最新hash,然后通过currentHash变量保存在本地。ok 事件会触发在 webpack/hot/dev-server.js 中定义的 module.hot.check 方法进行热更新检查。
module.hot.check
首先会利用上一次更新的 hash 值向服务端发送 ajax 请求,获取 name.hash.hot-update.json 文件。该文件中包含需要热更新的模块和下次热更新的 hash 值。
然后再通过 jsonp 的方式向服务端请求 name.hash.hot-update.js 文件,该 js 文件中包含的就是变更文件编译后的模块代码。当浏览器拿到 js 文件后会立刻执行 js 文件的代码。
该 js 文件中还包含一个 webpackHotUpdate 方法,webpackHotUpdate 方法主要做三个工作:
- 删除过期的模块和它相关依赖
- 将新的模块添加到 modules 中
- 通过 webpack_require 执行模块相关的代码
如果在热更新检查的过程中发生错误,导致热更新失败,最终会调用 window.location.reload 方法执行浏览器的刷新。
module.hot.accept
我们知道在 module.hot.check 方法中会通过 webpakc_require 执行更新模块相关的代码。但是执行代码的过程中并不会进行浏览器的渲染。
所以开发者应该在文件中手动添加当指定模块进行热更新后需要执行页面的代码。就是使用 module.hot.accept 方法指定模块路径和最新模块代码应用到运行环境的函数。