291 - 《HMR 和 React Refresh 原理》

发布于 2023年4月20日

近期可能会有较多和构建相关的内容调研。

HMR 基础

以 Webpack HMR 为例,看下 HMR 的 API 和使用场景。

// 接收自己更新,更新后重复执行自己,不往上冒泡
module.hot.accept();

// 接收依赖更新,更新后执行回调函数,不往上冒泡
module.hot.accept(['dep1'], () => {
  console.log('dep1 changed');
});

// 让自己失效,往上冒泡
// 通常在 accept 之后,遇到一些场景又希望自己失效时调用
module.hot.invalidate();

// 标记一些依赖为不可更新,这些依赖的更新会触发页面 reload
module.hot.decline(['dep1']);
// 同上,标记自己为不可更新
module.hot.decline();

// 设置或移除当前模块被自动替换时执行的回调函数
module.hot.dispose(fn);
module.hot.removeDisposeHandler(fn);

参考:
Hot Module Replacement - API | webpack
Hot Module Replacement - Guide | webpack

HMR 原理

Webpack 的文档讲得很详细了。

1、每个模块会做这些事,1)有 parents 和 children 属性,用来跟踪父子关系,2)给 hot API,包含前面说的那些功能,3)给两个方法 check 和 apply。
2、怎么获取更新?先 check 再 apply 。check 就是检查更新并下载更新的 module 和 chunk;apply 会,1)将所有更新模块标记为 invalid,2)每个模块分别检查他自己或父代模块是否有 accept handler,没有则刷新,有则冒泡到最先遇到的 accept handler 模块为止,3)dispose 和 unload 每个 invalid 的模块,4)执行所有 accept handler。
3、主要逻辑在 runtime,compiler 负责提供更新后的 module 和 chunk 列表

(注:图来自网络。)

简化后的 runtime 代码如下。

// __webpack_require__.i 是 module execution interceptor,require 模块时调用
__webpack_require__.i.push(options => {
	var module = options.module;
	var require = createRequire(options.require, options.id);
	module.hot = createModuleHotObject(options.id, module);
	module.parents = currentParents;
	module.children = [];
	currentParents = [];
	options.require = require;
});

// ...

1、createRequire 用来建立模块之间的父子管理,通过 parents 和 children 字段实现。
2、createModuleHotObject 返回 hot API,包含前面说的那些方法,比如 check、accept、invalidate、dispose 等

TODO:applyHandler 的逻辑代码在 JavascriptHotModuleReplacement.runtime.js 里。

参考:
Hot Module Replacement - Concepts | webpack
webpack/HotModuleReplacement.runtime.js at main · webpack/webpack · GitHub
webpack/JavascriptHotModuleReplacement.runtime.js at main · webpack/webpack · GitHub
Webpack HMR 原理解析 - 知乎 (有点过时

内容预览已结束

此内容需要会员权限。请先登录以查看完整内容。