• 什么是热更新?

无需刷新整个页面的同时更新模块

  • 作用

节省开发时间,提升开发体验

  • 疑问

文件变更,浏览器是怎么知道的

配置热更新

使用HotModuleReplacementPlugin插件或配置hot

  1. $ npm install webpack webpack-dev-server --save-dev
  1. devServer: {
  2. contentBase: path.resolve(__dirname, 'dist'),
  3. hot: true,
  4. historyApiFallback: true,
  5. compress: true
  6. },
  1. plugins: {
  2. HotModuleReplacementPlugin: new webpack.HotModuleReplacementPlugin()
  3. }, // 此处可以不写:因为如果devServer中配置hot:true,webpack会自动添加此插件

使用

  1. if (module.hot) {
  2. module.hot.accept();
  3. }

热更新原理

使用webpack-dev-server托管静态资源,同时以runtime方式注入客户端代码
浏览器加载页面后,建立websocket链接
webpack监听文件的变化,构建发生变更的的模块,确认变更范围
浏览器加载变更的模块
webpack运行时出发变更的module.hot.accept回调,执行变更逻辑

1. webpack-dev-server启动本地服务

  1. 根据scriptwebpack-dev-server命令在bin中找到外链,看看具体执行的文件,以我自己的项目为例,最终在<br />// node_modules/@webpack-cli/serve/bin/index.js
let compiler = webpack(config); // 1. 生成webpack编译主引擎,compiler可以启动webpack所有编译工作,以及监听本地文件的变化
// 2.启动本地服务
server = new Server(compiler, devServerOptions);
 server.listen(devServerOptions.port, devServerOptions.host, (error) => {
                            if (error) {
                                throw error;
                            }
                        });
// node_modules/webpack-dev-server/lib/Server.js
class Server {
    constructor() {
        this.setupApp();
        this.createServer();
    }

    setupApp() {
        // 依赖了express
        this.app = new express(); // 使用express启动本地服务,让浏览器请求本地的静态资源
    }

    createServer() {
        this.listeningApp = http.createServer(this.app);
    }
    listen(port, hostname, fn) {
        return this.listeningApp.listen(port, hostname, (err) => {
            // 3. 启动express服务后,启动websocket服务,websocket可以建立本地和浏览器的双向通信,这样就可以实现当本地文件发生变化,立马告诉浏览器可以热更新代码啦
            this.createSocketServer();
        }
    }                                   
}

监听webpack编译结束

// node_modules/webpack-dev-server/lib/Server.js
// 绑定监听事件
setupHooks() {
    const {done} = compiler.hooks;
    // 监听webpack的done钩子,tapable提供的监听方法
    done.tap('webpack-dev-server', (stats) => {
      // 调用_sendStats,通过websocket给浏览器发送通知 ok和hash事件,这里可以拿到hash值,做检查更新逻辑
        this._sendStats(this.sockets, this.getStats(stats));
        this._stats = stats;
    });
};


// 通过websoket给客户端发消息
_sendStats() {
    this.sockWrite(sockets, 'hash', stats.hash);
    this.sockWrite(sockets, 'ok');
}

监听文件的变化

每次修改代码,就会出发更新,因此需要监听本地代码的变化
主要是webpack-dev-middleware库实现的:本地文件的编译输出以及监听

// node_modules/webpack-dev-middleware/index.js
compiler.watch(options.watchOptions, (err) => {
    if (err) { /*错误处理*/ }
});

// 通过“memory-fs”库将打包后的文件写入内存
setFs(context, compiler);

启动阶段

  • webpack Compiler将对应文件打包成bundle.js(包含注入的HMR Server),发送给Bundler Server
  • 浏览器即可访问服务器的方式去获取bundle.js

    更新阶段

  • webpack compiler重新编译,发送给HMR Server

  • HMR Server可以知道有哪些资源、哪些模块发生了变化,通知HRM Runtime
  • HRM Runtime更新代码