Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack 热更新(HMR)实现原理 #86

Open
bojue opened this issue Apr 12, 2020 · 0 comments
Open

webpack 热更新(HMR)实现原理 #86

bojue opened this issue Apr 12, 2020 · 0 comments
Labels

Comments

@bojue
Copy link
Owner

bojue commented Apr 12, 2020

HMR(Hot Module Replacement)是webpack一个重要的特性,当代码文件修改并保存之后,webapck通过watch监听到文件发生变化,会对代码文件重新打包生成两个模块补丁文件manifest(js)和一个(或多个)updated chunk(js),将结果存储在内存文件系统中,通过websocket通信机制将重新打包的模块发送到浏览器端,浏览器动态的获取新的模块补丁替换旧的模块,浏览器不需要刷新页面就可以实现应用的更新。

优点:

  • 代码文件修改到页面内容更新,自动完成
  • 兼容目前市面上主流的开发框架 :react,vue,angular2,如使用angular-cli创建ng项目通过@ngtools/webpack已经内置了webpack
  • 相比location.reload() 更新方式,不需要刷新页面,可以保存应用的当前状态

HMR相关的中间件

  • webpack-dev-middleware

本质上是一个容器,将webpack处理后的文件传递个服务器。

webpack-dev-middleware 是一个 express 中间件,核心实现两个功能:第一通过file-loader内部集成了node的 monery-fs/memfs 内部文件系统,,直接将资源存储在内存;第二是通过watch监听文件的变化,动态编译。

  • webpack-hot-middleware

核心是给webpack提高服务端和客户端之间的通信机制,内部使用windoe.EventSocurce实现。

在webpack第一次打包的时候,除了代码本身之外,还包含一部分HMRruntime订阅服务代码,HMRruntime 订阅服务端的更新变化,触发HMR runtime API拉取最新的资源模块。

webpack-hot-middleware实现页面的热重载。

  • webpack-dev-server

内置了webpack-dev-middleware和express服务器,利用webpack-dev-middleware提供文件的监听和编译,利用express提供http服务,底层利用websocket代替EventSource实现了webpack-hot-middleware提供的客户端和服务器之间的通信机制。

HMR的工作原理

图片

  1. webpack --watch启动监听模式之后,webpack第一次编译项目,并将结果存储在内存文件系统,相比较磁盘文件读写方式内存文件管理速度更快,内存webpack服务器通知浏览器加载资源,浏览器获取的静态资源除了JS code内容之外,还有一部分通过webpack-dev-server注入的的 HMR runtime代码,作为浏览器和webpack服务器通信的客户端( webpack-hot-middleware 提供类似的功能)。截图如下:

image

  1. 文件系统中一个文件(或者模块)发生变化,webpack监听到文件变化对文件重新编译打包,每次编译生成唯一的hash值,根据变化的内容生成两个补丁文件:说明变化内容的manifest(文件格式是hash.hot-update.json,包含了hash和chundId用来说明变化的内容)和chunk js(hash.hot-update.js)模块。

image

  1. hrm-server通过websocket将manifest推送给浏览器

浏览器接受到最新的 hotCurrentHash,触发 hotDownloadManifest函数,获取manifest json 文件。

function hotDownloadManifest() {
    var request = new XMLHttpRequest();
    var requestPath = __webpack_require__.p + "" + hotCurrentHash + ".hot-update.json";
    request.open("GET", requestPath, true);		
    request.send(null);
}

image

  1. 浏览器端hmr runtime根据manifest的hash和chunkId使用ajax拉取最新的更新模块chunk
function hotDownloadUpdateChunk(chunkId) {
    var script = document.createElement("script");
    script.src = __webpack_require__.p + "" 
                     + chunkId + "." + hotCurrentHash + ".hot-update.js";	
    document.head.appendChild(script);
}

image

image

  1. 触发render流程实现局部热重载
    HMR runtime 调用window["webpackHotUpdate"] 方法,调用hotAddUpdateChunk
var parentHotUpdateCallback = window["webpackHotUpdate"];
window["webpackHotUpdate"] = function webpackHotUpdateCallback(chunkId, moreModules) {
    hotAddUpdateChunk(chunkId, moreModules);
    if (parentHotUpdateCallback) parentHotUpdateCallback(chunkId, moreModules);
};

hotAddUpdateChunk动态的更新代码模块,并调用hotUpdateDownloaded函数

function hotAddUpdateChunk(chunkId, moreModules) {
    hotRequestedFilesMap[chunkId] = false;
    for (var moduleId in moreModules) {
        if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
            hotUpdate[moduleId] = moreModules[moduleId]; 			}
    }
    if (--hotWaitingFiles === 0 && hotChunksLoading === 0) {
        hotUpdateDownloaded();
    }
}

hotUpdateDownloaded执行hotApply执行热重载

function hotUpdateDownloaded() {
    hotSetStatus("ready");
    Promise.resolve()
        .then(function() {
            return hotApply(hotApplyOnUpdate);
        })
}

参考

@bojue bojue changed the title webpack实现原理 webpack 热更新(HRM)实现原理 Apr 23, 2020
@bojue bojue changed the title webpack 热更新(HRM)实现原理 webpack 热更新(HMR)实现原理 Apr 26, 2020
@bojue bojue added the Tool label Apr 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant