【Vite】模块热替换 HMR

概述

Vite 提供了一套原生 ESM 的 HMR API。 具有 HMR 功能的框架可以利用该 API 提供即时、准确的更新,而无需重新加载页面或清除应用程序状态。当通过 create-vite 创建应用程序时,所选模板已经预先配置了相关的集成。

HMR API

Vite 通过特殊的 import.meta.hot 对象暴露手动 HMR API。

interface ImportMeta {
  readonly hot?: ViteHotContext
}

interface ViteHotContext {
  readonly data: any

  accept(): void
  accept(cb: (mod: ModuleNamespace | undefined) => void): void
  accept(dep: string, cb: (mod: ModuleNamespace | undefined) => void): void
  accept(
    deps: readonly string[],
    cb: (mods: Array<ModuleNamespace | undefined>) => void,
  ): void

  dispose(cb: (data: any) => void): void
  prune(cb: (data: any) => void): void
  invalidate(message?: string): void

  on<T extends string>(
    event: T,
    cb: (payload: InferCustomEventPayload<T>) => void,
  ): void
  off<T extends string>(
    event: T,
    cb: (payload: InferCustomEventPayload<T>) => void,
  ): void
  send<T extends string>(event: T, data?: InferCustomEventPayload<T>): void
}

代码结构概述

代码中定义了两个接口:ImportMetaViteHotContextImportMeta 是在模块上下文中使用的,ViteHotContext 则是 Vite 的 HMR 相关的接口,包含了一些允许你在模块更新时执行特定操作的方法。

ImportMeta 接口

interface ImportMeta {
  readonly hot?: ViteHotContext;
}

hot 属性是 ViteHotContext 类型的可选属性。当 Vite 在开发模式下运行时,这个属性会被填充,用于处理 HMR 相关操作。如果你在生产环境或 HMR 未启用的情况下访问 import.meta.hot,它将是 undefined

ViteHotContext 接口

ViteHotContext 定义了与 HMR 相关的各种方法,这些方法用于处理模块的更新、清理以及自定义事件。

方法说明
  1. accept()

    • 用于接受模块更新。当模块更新时,Vite 将使用最新的模块重新加载。你可以提供一个回调函数来处理更新后的模块,或仅调用 accept() 以自动接受更新。
    • 示例:
      if (import.meta.hot) {
        import.meta.hot.accept((mod) => {
          console.log('模块更新了', mod);
        });
      }
      
  2. dispose(cb)

    • 注册一个回调函数,当模块被替换或页面刷新时执行。可以在这里进行资源清理或保存一些状态信息。
    • 示例:
      if (import.meta.hot) {
        import.meta.hot.dispose((data) => {
          data.someState = currentState;
        });
      }
      
  3. prune(cb)

    • 当模块被 HMR 剪枝时(意味着模块将不再被使用),会调用这个回调函数。可以用它来处理一些清理工作。
    • 示例:
      if (import.meta.hot) {
        import.meta.hot.prune(() => {
          console.log('模块已被剪枝');
        });
      }
      
  4. invalidate(message)

    • 强制使当前模块失效,并触发 HMR 更新。可以选择传递一个信息字符串,说明为什么需要失效。
    • 示例:
      if (import.meta.hot) {
        import.meta.hot.invalidate('模块需要更新');
      }
      
  5. on(event, cb)off(event, cb)

    • on 方法用于监听自定义的 HMR 事件,off 方法用于取消监听这些事件。可以通过 send 方法发送自定义事件。
    • 示例:
      if (import.meta.hot) {
        import.meta.hot.on('my-custom-event', (payload) => {
          console.log('收到自定义事件', payload);
        });
      }
      
  6. send(event, data)

    • 发送自定义 HMR 事件,并附带数据。
    • 示例:
      if (import.meta.hot) {
        import.meta.hot.send('my-custom-event', { someData: 123 });
      }
      

结合示例说明

假设在一个项目中使用了一个组件,并且希望在组件更新时能保存它的状态,代码如下:

<script setup>
let currentState = {count: 0}; // 初始化状态

if (import.meta.hot) {
  // 检查是否存在之前保存的状态,并进行恢复
  if (import.meta.hot.data && import.meta.hot.data.currentState) {
    currentState = import.meta.hot.data.currentState;
  }

  // 接受模块更新
  import.meta.hot.accept((mod) => {
    console.log('模块更新了', mod);
  });

  // 保存当前状态,确保在模块被替换前调用
  import.meta.hot.dispose((data) => {
    data.currentState = currentState;
  });
}

function increment() {
  currentState.count += 1;
  console.log(currentState.count);
}

</script>

<template>
  <button @click="increment">count++</button>
</template>

<style scoped>

</style>

在这个示例中,通过 dispose 方法保存组件的状态,并在模块更新后通过 accept 方法重新加载模块时恢复该状态。这就避免了在模块热更新时丢失状态。

项目运行:

在这里插入图片描述

模块更新:

在这里插入图片描述

如果将外部引入的模块删掉并保存,则会显示 page reload,也就是页面重新刷新了一下。

hmr 只会在开发环境生效( 生产环境 import.meta.hot 为 undefined ),在生产环境里边它是不存在的。就会被 tree shaking 给优化掉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秀秀_heo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值