likes
comments
collection
share

webpack5 热更新从配置到原理

作者站长头像
站长
· 阅读数 45

概述

项目里用到的刷新方案有两种,一般为了区分称为热更新和热重载

  • 热重载 : 顾名思义重新加载,使用 window.location.reload() 刷新界面以达到更新的目的
  • 热更新:只更新修改的文件。不会刷新界面。

Hot Module Replacement(或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新各种模块,而无需完全刷新。 - 官网

不管热重载和热更新都能很大程度提高我们的开发效率。下面将由浅入深的的梳理下热更新的知识点,而热重载相关知识比较简单,会在热更新的解释中提及。

了解热更新的价值

在我的平时工作中在了解热更新的机制后,在一下几个方面会有用到的机会。

  • 搭建自己的项目,在 webpack 配置中使用
  • 搭建自己的cli,将自己的 webpack 配置封装到cli中,参考 vue-cli
  • 搭建自己的SSR框架时,参考 Nuxt
  • 面试涨薪,走上人生巅峰。

热更新的实现

这边建议在看下面内容的时候先看一遍 官网 相关的知识,下面的内容都是以 官网 的信息为基础进行讲解的。

此文不赘述官网上面的相关知识了。下面主要介绍 通过 Node.js API 来搭建 HMR 的过程的拓展。​

官网 是将 Webpack Dev ServerNode.js API 一起使用,来实现 HMR 的搭建的,为了更好的理解 HMR 的原理,这里我们使用 更深一步的 middleware pluginNode.js API 来实现 HMR

首先简单了解下将要用到的几种 middlewareplugin

  • webpack-hot-middleware: 该模块只关注将浏览器客户端连接到 webpack 服务器并接收更新的机制。
  • webpack-dev-middleware: 是一个容器(wrapper),它可以把webpack处理后的文件传递给一个服务器(server)。
  • webpack.HotModuleReplacementPlugin: 用于生成热更新的相关文件。

下面进行实操将这些 middlewareplugin 进行组合,以实现 HMR。首先我们基于官网的例子, 新建出以下所示的目录具体代码的github地址

├─package.json
├─yarn.lock
├─src
|  ├─index.js - 同官网的例子
|  └print.js
├─server
|   └dev-server.js 
├─bundle
|   └webpack.config.js

package.json

		"express": "^4.17.1",
    "html-webpack-plugin": "^5.3.1",
    "webpack": "^5.39.1",
    "webpack-cli": "^4.7.2",
    "webpack-dev-middleware": "^5.0.0",
    "webpack-hot-middleware": "^2.25.0"

webpack.config.js

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

  module.exports = {
    mode: "development",
    entry: {
       index: './src/index.js',
    },
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, '../dist'),
        clean: true,
    },
    plugins: [
      new HtmlWebpackPlugin(),
    ]
  };

dev-server.js

const config = require("../bundle/webpack.config"); 
const express = require('express');
const app = express();
const webpack = require("webpack");

// 当前的环境是开发环境
// 修改入口文件,用于热更新通信
config.entry.index = ["webpack-hot-middleware/client", config.entry.index];
// 用与生成热更新的 lastHash.hot-update.json 和 chunkID.lastHash.hot-update.js
config.plugins.push(new webpack.HotModuleReplacementPlugin());
// 准备好热更新的环境之后开始编译
const compiler = webpack(config);
// webpack-dev-middleware 是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server。
const devMiddleware = require("webpack-dev-middleware")(compiler, {
    publicPath: config.output.publicPath,
    serverSideRender: true 
});

app.use(devMiddleware)

app.use(require("webpack-hot-middleware")(compiler));
compiler.hooks.done.tap("done", stats => {
    const info = stats.toJson();
    if (stats.hasWarnings()) {
        console.warn(info.warnings);
    }

    if (stats.hasErrors()) {
        console.error(info.errors);
        return;
    }
    console.log("打包完成");
});

app.listen(9527, () => {
    console.log("Your app is running", 9527);
});

这个时候我们就实现了类似官网的 HMR 下面我们就基于这个 demo 来浅谈下 HRM 的原理。

热更新的原理

热更新在编译阶段和界面上的体现

当我们编译的时候,可以直观的看到生成了 index.37b9afad78a811b0d250.hot-update.jsindex.37b9afad78a811b0d250.hot-update.json这两个与 HMR 相关的两个文件。webpack5 热更新从配置到原理在浏览器上,发现发送了两个请求webpack5 热更新从配置到原理分别用来获取这两个文件。返回的内容如下​

index.37b9afad78a811b0d250.hot-update.json

{"c":["index"],"r":[],"m":[]}

index.37b9afad78a811b0d250.hot-update.js


self["webpackHotUpdateuse"]("index", {
    "./src/print.js":
    ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
        "use strict";
        __webpack_require__.r(__webpack_exports__);
        __webpack_require__.d(__webpack_exports__, {
            "default": () => (/* binding */ printMe)});
            function printMe() {
                console.log('Updating print.js2..');
            }//# sourceURL=webpack://use/./src/print.js?");
    })},
     function(__webpack_require__) {
     "use strict";
        (() => {
            __webpack_require__.h = () => ("f4e40048658a6c48ff32")
        })();
     }
   );

看到上面的现象之后,大概有许多疑惑,下面带着这些疑惑继续往下看。

热更新的原理讲解

带着上面的问题,我们来探索热更新的意义,首先我们看下热更新的整体流程webpack5 热更新从配置到原理

  1. 首先启动 dev-server, 往当前的 options.entry 中插入相关的 SSE 相关的代码。

注入 HTML 5 服务器发送事件 ( SSE, server-sent event ) ,和 心跳检测的相关机制,主要是为了服务器和客户端的通信,当服务器文件发生变化的时候能够方便通知到客户端。

  1. 监听 webpack donecompiler.hooks.done.tap 节点从 stats 中获取到最新的 hash 值,传递给客户端,如果只是做热重载到这步就可以结束了,使用 window.location.reload();
  2. 客户端获取 lastHash.hot-update.json 文件,获取到当前更新了的文件的 chunkId 。
  3. 根据当前更新的 chunkId 去拿 chunkID.lastHash.hot-update.js 文件
  4. 执行 webpackHotUpdate, 从 现有的 cache 中 找到当前的 chunkId 对应的旧的数据信息,进行更新并执行当前最新的 chunk 代码,以便于更新 cache,接着执行对应的 hot.accept 代码来实现 render 操作。

总结

本文没有使用 webpackDevServer,其实 webpackDevServer 就是内置了 Webpack-dev-middlewareExpress 服务器,以及利用 websocket 替代 SSE 来实现webpack-hot-middleware 的逻辑。原理的部分也是帮助大家从整体上认识了 HMR。希望通过这篇文章,可以将热更新活学活用,如果需要了解每一步的具体细节,可以按照这个思路去看看源码。

转载自:https://juejin.cn/post/6976082291428704292
评论
请登录