CC 4.0 许可证

本节内容来自以下链接的内容,并受 CC BY 4.0 许可证的约束。

如果没有特别说明,以下内容可以被认为是基于原始内容进行修改和删除的结果。

热模块替换

通常,用户会检查界面是否可访问,然后开始使用它。例如,以下是如何使用 accept 更新模块的示例

if (module.hot) {
  module.hot.accept('./library.js', function () {
    // Do something with the updated library module...
  });
}

// or
if (import.meta.webpackHot) {
  import.meta.webpackHot.accept('./library.js', function () {
    // Do something with the updated library module…
  });
}

支持以下方法...

模块 API

accept

接受对给定 dependencies 的更新并触发 callback 以对这些更新做出反应,此外,还可以附加一个可选的错误处理程序

module.hot.accept(
  dependencies, // Either a string or an array of strings
  callback, // Function to fire when the dependencies are updated
  errorHandler, // (err, {moduleId, dependencyId}) => {}
);

// or
import.meta.webpackHot.accept(
  dependencies, // Either a string or an array of strings
  callback, // Function to fire when the dependencies are updated
  errorHandler, // (err, {moduleId, dependencyId}) => {}
);

使用 ESM import 时,来自 dependencies 的所有导入符号都会自动更新。注意:依赖项字符串必须与 import 中的 from 字符串完全匹配。在某些情况下,甚至可以省略 callback。在 callback 中使用 require() 在这里没有意义。

使用 CommonJS 时,需要使用 callback 中的 require() 手动更新依赖项。在这里省略 callback 没有意义。

accept 的错误处理程序

(err, {moduleId, dependencyId}) => {}

  • err:在使用 ESM 依赖项时,在第二个参数中或依赖项执行期间由回调抛出的错误。
  • moduleId:当前模块 ID。
  • dependencyId:已更改依赖项(第一个)的模块 ID。

accept (self)

接受对自身的更新。

module.hot.accept(
  errorHandler, // Function to handle errors when evaluating the new version
);

// or
import.meta.webpackHot.accept(
  errorHandler, // Function to handle errors when evaluating the new version
);

当此模块或依赖项更新时,此模块可以被丢弃并重新评估,而无需通知父级。如果此模块没有导出(或导出以其他方式更新),则此操作是有意义的。

当此模块(或依赖项)的评估抛出异常时,会触发 errorHandler

自接受的错误处理程序

(err, {moduleId, module}) => {}

  • err:评估新版本时的错误。
  • moduleId:当前模块 ID。
  • module:当前模块实例。
    • module.hot:允许使用出错模块实例的 HMR API。一个常见场景是再次自接受它。为传递数据添加处理程序也是有意义的。注意,出错的模块可能已部分执行,因此请确保不要进入不一致的状态。可以使用 module.hot.data 存储部分状态。
    • module.exports:可以被覆盖,但要注意,属性名称可能在生产模式下被混淆。

decline

拒绝对给定 dependencies 的更新,强制更新失败并显示 'decline' 代码。

module.hot.decline(
  dependencies, // Either a string or an array of strings
);

// or
import.meta.webpackHot.decline(
  dependencies, // Either a string or an array of strings
);

将依赖项标记为不可更新。当更改此依赖项的导出无法处理或处理尚未实现时,这很有意义。根据您的 HMR 管理代码,对这些依赖项(或其未接受的依赖项)的更新通常会导致页面完全重新加载。

decline (self)

拒绝对自身的更新。

module.hot.decline();

// or
import.meta.webpackHot.decline();

将此模块标记为不可更新。当此模块具有不可逆的副作用,或尚未为此模块实现 HMR 处理时,这很有意义。根据您的 HMR 管理代码,对此模块(或未接受的依赖项)的更新通常会导致页面完全重新加载。

dispose(或 addDisposeHandler)

添加一个处理程序,该处理程序在替换当前模块代码时执行。这应该用于删除您已声明或创建的任何持久性资源。如果要将状态转移到更新的模块,请将其添加到给定的 data 参数中。此对象将在更新后在 module.hot.data 中可用。

module.hot.dispose(data => {
  // Clean up and pass data to the updated module...
});

// or
import.meta.webpackHot.dispose(data => {
  // Clean up and pass data to the updated module...
});

invalidate

调用此方法将使当前模块无效,这会在应用 HMR 更新时将其丢弃并重新创建。这就像此模块的正常更新一样冒泡。invalidate 无法被此模块自接受。

idle 状态期间调用时,将创建一个包含此模块的新 HMR 更新。HMR 将进入 ready 状态。

readyprepare 状态期间调用时,此模块将被添加到当前 HMR 更新中。

check 状态期间调用时,此模块将在更新可用时被添加到更新中。如果没有更新可用,它将创建一个新的更新。HMR 将进入 ready 状态。

disposeapply 状态期间调用时,HMR 将在退出这些状态后将其拾取。

用例

条件接受

模块可以接受依赖项,但可以在无法处理依赖项更改时调用 invalidate

import { x, y } from './dep';
import { processX, processY } from 'anotherDep';

const oldY = y;

processX(x);
export default processY(y);

module.hot.accept('./dep', () => {
  if (y !== oldY) {
    // This can't be handled, bubble to parent
    module.hot.invalidate();
    return;
  }
  // This can be handled
  processX(x);
});

条件自接受

模块可以自接受,但可以在无法处理更改时使自身无效

const VALUE = 'constant';

export default VALUE;

if (
  module.hot.data &&
  module.hot.data.value &&
  module.hot.data.value !== VALUE
) {
  module.hot.invalidate();
} else {
  module.hot.dispose(data => {
    data.value = VALUE;
  });
  module.hot.accept();
}

触发自定义 HMR 更新

const moduleId = chooseAModule();
const code = __webpack_modules__[moduleId].toString();
__webpack_modules__[moduleId] = eval(`(${makeChanges(code)})`);
if (require.cache[moduleId]) {
  require.cache[moduleId].hot.invalidate();
  module.hot.apply();
}

T> 当调用 invalidate 时,dispose 处理程序最终将被调用并填充 module.hot.data。如果未注册 dispose 处理程序,则将向 module.hot.data 提供一个空对象。

W> 不要通过反复调用 invalidate 陷入 invalidate 循环。这会导致堆栈溢出,HMR 进入 fail 状态。

removeDisposeHandler

删除通过 disposeaddDisposeHandler 添加的处理程序。

module.hot.removeDisposeHandler(callback);

// or
import.meta.webpackHot.removeDisposeHandler(callback);

管理 API

status

检索热模块替换过程的当前状态。

module.hot.status(); // Will return one of the following strings...

// or
import.meta.webpackHot.status();
状态 描述
idle 进程正在等待对 check 的调用
check 进程正在检查更新
prepare 进程正在为更新做好准备(例如,下载更新的模块)
ready 更新已准备就绪,可以获得
dispose 进程正在调用将被替换的模块上的 dispose 处理程序
apply 进程正在调用 accept 处理程序并重新执行自接受的模块
abort 更新已中止,但系统仍处于其先前状态
fail 更新已抛出异常,并且系统的状态已受到损害

check

测试所有加载的模块以进行更新,如果存在更新,则 apply 它们。

module.hot
  .check(autoApply)
  .then(outdatedModules => {
    // outdated modules...
  })
  .catch(error => {
    // catch errors
  });

// or
import.meta.webpackHot
  .check(autoApply)
  .then(outdatedModules => {
    // outdated modules...
  })
  .catch(error => {
    // catch errors
  });

autoApply 参数可以是布尔值或 options,在调用时传递给 apply 方法。

应用

继续更新过程(只要 module.hot.status() === 'ready')。

module.hot
  .apply(options)
  .then(outdatedModules => {
    // outdated modules...
  })
  .catch(error => {
    // catch errors
  });

// or
import.meta.webpackHot
  .apply(options)
  .then(outdatedModules => {
    // outdated modules...
  })
  .catch(error => {
    // catch errors
  });

可选的 options 对象可以包含以下属性

  • ignoreUnaccepted (布尔值):忽略对未接受模块所做的更改。
  • ignoreDeclined (布尔值):忽略对已拒绝模块所做的更改。
  • ignoreErrored (布尔值):忽略在接受处理程序、错误处理程序和重新评估模块时抛出的错误。
  • onDeclined (函数(info)):已拒绝模块的通知器
  • onUnaccepted (函数(info)):未接受模块的通知器
  • onAccepted (函数(info)):已接受模块的通知器
  • onDisposed (函数(info)):已处置模块的通知器
  • onErrored (函数(info)):错误的通知器

info 参数将是一个对象,其中包含以下一些值

{
  type: 'self-declined' | 'declined' |
        'unaccepted' | 'accepted' |
        'disposed' | 'accept-errored' |
        'self-accept-errored' | 'self-accept-error-handler-errored',
  moduleId: 4, // The module in question.
  dependencyId: 3, // For errors: the module id owning the accept handler.
  chain: [1, 2, 3, 4], // For declined/accepted/unaccepted: the chain from where the update was propagated.
  parentId: 5, // For declined: the module id of the declining parent
  outdatedModules: [1, 2, 3, 4], // For accepted: the modules that are outdated and will be disposed
  outdatedDependencies: { // For accepted: The location of accept handlers that will handle the update
    5: [4]
  },
  error: new Error(...), // For errors: the thrown error
  originalError: new Error(...) // For self-accept-error-handler-errored:
                                // the error thrown by the module before the error handler tried to handle it.
}

addStatusHandler

注册一个函数来监听 status 的变化。

module.hot.addStatusHandler(status => {
  // React to the current status...
});

// or
import.meta.webpackHot.addStatusHandler(status => {
  // React to the current status...
});

请记住,当状态处理程序返回一个 Promise 时,HMR 系统将等待 Promise 解决后再继续。

removeStatusHandler

删除已注册的状态处理程序。

module.hot.removeStatusHandler(callback);

// or
import.meta.webpackHot.removeStatusHandler(callback);