-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
第 70 题: 介绍下 webpack 热更新原理,是如何做到在不刷新浏览器的前提下更新页面 #118
Comments
1.当修改了一个或多个文件; |
给大家讲个背景,sean larkin(webpack core developer)提过一个事: |
你们这些人我服了,这原连接地址,我真的不知道说什么好 |
简单来说就是:hot-module-replacement-plugin 包给 webpack-dev-server 提供了热更新的能力,它们两者是结合使用的,单独写两个包也是出于功能的解耦来考虑的。 |
当时略微看了一下HMR的代码Webpack4和实现,主要要点在于:
可以参见一个不太成熟的源码解析: |
为什么HMR用webSocket通知需要更新时不直接把更新后的模块信息发过去呢?还要多一步HMR runtime主动去请求 |
太长了 能用10句话内总结下吗 |
看得有点蒙 |
还记得 HMR 的工作原理图解 中的问题 3 吗?为什么更新模块的代码不直接在第三步通过 websocket 发送到浏览器端,而是通过 jsonp 来获取呢?我的理解是,功能块的解耦,各个模块各司其职,dev-server/client 只负责消息的传递而不负责新模块的获取,而这些工作应该有 HMR runtime 来完成,HMR runtime 才应该是获取新代码的地方。再就是因为不使用 webpack-dev-server 的前提,使用 webpack-hot-middleware 和 webpack 配合也可以完成模块热更新流程,在使用 webpack-hot-middleware 中有件有意思的事,它没有使用 websocket,而是使用的 EventSource。综上所述,HMR 的工作流中,不应该把新模块代码放在 websocket 消息中。 |
关于webpack的热更新原理,面试官比较想听到的是工作流程和关键点,非“流水账”式的源码分析。我认为可以这样的介绍: 首先,介绍webpack-dev-server: 其次,介绍工作流程: 欢迎拍砖! |
在 webpack 中,都是模块且有唯一标识。在 webpack 编译完成后,将修改的模块 hash 对应的模块重新执行。就达到了局部刷新的效果。 过程
|
上面说的都太复杂了,一句话:
有个前提是模块的名称不会变化,所以在开发期间的webpack配置的chunkId不能是hash,因为模块名称如果变化,webpack脚手架就找不到新的文件在浏览器中模块的位置,自然无法局部更新这个模块。 但是在部署生产环境的的时候,为了防止浏览器缓存模块,一般是要做hash处理的。如果使用现成的什么create-react/vue之类的脚手架就不用关心这个,因为别人已经帮你处理好了,如果自己手动搭建要注意。 |
所以知道了之后,能做什么呢?装逼吗?记住几个步骤,然后背出来?为了知道而知道?还是说你遇到了问题才去研究,自己去看? |
上面有几个评论只是在介绍步骤,能再深入点吗?感觉就像是在介绍代码调用了哪些函数,都没有深入解析这些函数内部的实现原理。 我觉得这问题可以换一种问法:有 A、B 两个 js 模块,现要将当前使用的 A 模块替换成 B 模块,如何用最简单的代码实现热更新? |
|
我现在在自己写一个比较简陋的demo。发现文件变动,建立websocket,浏览器更新对应模块都好说。但是最大的问题是更新了模块之后呢?如果只是重新__require__该模块没办法刷新页面,引入之后只是返回了一个对象,里面的函数不知道运行哪一个。不知道是不是我流程搞错了,希望有大佬能解答一下 |
掘金政采云有篇文章写的很好,整体流程大概如下: WDS 启动本地服务 (new webpack --> 启动 server --> 启动 websocket{将 websocket 的代码注入到浏览器代码中}
--> webpack 开始监听文件变动{变动了就重新编译构建} --> HMR-Plugin 将热更新代码注入到 浏览器运行代码中,也就是 HRM runtime)
--> HRM runtime 删除过期的模块,替换为新的模块,然后开始执行相关代码 |
网上的这些关于HMR的文章都是为了博眼球吸流量的,放下浮躁的心好好看看源码
webpack.HotModuleReplacementPlugin负责比对module的hash生成
核心代码
hotUpdateMainContentByFilename.set(filename, {
removedChunkIds,
removedModules,
updatedChunkIds,
assetInfo
});
webpack-hot-middleware负责获取HotModuleReplacementPlugin对比出来的chunk信息丢给客户端
服务端核心代码
function onDone(statsResult) {
if (closed) return;
// Keep hold of latest stats so they can be propagated to new clients
latestStats = statsResult; // 取到最新的chunk的stats信息
publishStats('built', latestStats, eventStream, opts.log);
}
compiler.hooks.done.tap('webpack-hot-middleware', onDone); // webpack的插件钩子,一次更新编译后完成调用
eventStream.publish({ // 通过EventSource长链接推送给前端,(这里还有很多逗比说HMR是websocket,sock.js实现的)
name: name,
action: action,
time: stats.time, // stats 就是 onDone 取到的chunk信息
hash: stats.hash,
warnings: stats.warnings || [],
errors: stats.errors || [],
modules: buildModuleMap(stats.modules),
});
客户端核心代码
var applyResult = module.hot.apply(applyOptions, applyCallback); // 接收到变更的chunk信息
在webpack包下的hrm目录下有个LazyCompilationPlugin.js文件
核心代码
Template.indent([
"module.hot.accept();",
`module.hot.accept(${JSON.stringify(
chunkGraph.getModuleId(module)
)}, function() { module.hot.invalidate(); });`,
"module.hot.dispose(function(data) { delete data.resolveSelf; dispose(data); });",
"if (module.hot.data && module.hot.data.resolveSelf) module.hot.data.resolveSelf(module.exports);" // 得到最新的chunk代码
]),
总结起来就是:webpack.HotModuleReplacementPlugin负责生成变更的chunk信息 -> webpack-hot-middleware负责接受到这些信息然后通过EventSource丢给客户端 -> 同时webpack-hot-middleware也负责接受chunk信息传递给module.hot链 -> webpack通过hot.accept传入文件名获取对应的moduleId然后在hot.data中取代码
以上是花费30分钟看webpack源码的结果,以后有时间有机会写文章详述 |
来着chatGPT的回答: 实现 HMR 的原理主要有以下几个步骤:
需要注意的是,HMR 需要在代码中进行特殊的标记,以便在编译时生成对应的 HMR Runtime 代码,使得浏览器端能够正确地接收和替换更新的模块。同时,由于 HMR 只替换更新模块,因此对于一些全局状态的修改,仍然需要手动刷新页面才能生效。 总之,Webpack 热更新通过实现模块的替换更新,从而避免了页面的刷新,提高了开发效率和用户体验。 |
参考链接
The text was updated successfully, but these errors were encountered: