npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

microapp-hmr

v1.0.9

Published

Module-level HMR for MicroApp/UMD: EventSource + fetch/eval hot-update, component change analysis, MicroApp.reload(). Webpack 4/5.

Readme

microapp-hmr

模块级热更新运行时 · 为微前端架构打造的增量开发利器

npm · MIT

代码保存 → 浏览器自动感知 → 只重载受影响的子应用,整页不刷新

快速开始 · 特性 · 配置 · 常见问题


📖 简介

microapp-hmr 是为 MicroApp/UMD 场景设计的模块级热更新解决方案。它让微前端架构下的开发体验飞跃提升——修改子应用代码后,只有受影响的子应用自动重载,宿主页面保持不刷新,开发效率成倍提升。

核心特性

  • 🚀 真正的模块级 HMR:代码保存 → 自动感知 → 只重载受影响的子应用
  • 🔧 零侵入业务代码:通过轻量级 monkey patch 接入,异常时自动降级
  • 📊 可观测性:内置控制台面板,实时展示变更文件、影响的子应用和组件
  • 🛡️ 安全可靠:构建失败自动跳过 HMR,仅限开发环境使用
  • 🔌 多工程支持:一个 devServer + 多个仅构建工程,通过 Broker 统一热更新

设计理念

当前采用整包重载而非细粒度组件级热更,是基于微前端架构的理性选择:

  • 子应用为独立 bundle,不共享宿主模块图
  • 无常驻运行时做细粒度补丁
  • 整包重载侵入最小、行为可预期
  • 内置静态分析追踪「文件变更影响的组件」,便于排查

未来可在此基础上探索组件级 HMR,保留组件 state、只替换变更模块。


📦 安装

# 作为开发依赖安装(仅开发态使用)
npm install microapp-hmr -D

安全警告

  • HMR 运行时通过 fetch + new Function 执行远端脚本,仅限可信内网开发环境。请勿在公网或不可信环境开启 HMR。
  • enhance-dev-server.cjs 中挂载的 /__webpack_hmr/__microapp_hmr_publish__ 端点同样只面向本地/内网开发,如需在多工程场景下使用 Broker,请确保这些端点不暴露到生产环境,并在需要时配置 MICROAPP_HMR_BROKER_TOKEN 做简单鉴权。

🚀 快速开始

前提条件

  • webpack 4 或 webpack 5 + HotModuleReplacementPlugin
  • webpack-hot-middleware
  • 宿主应用提供 window.__HMR_RELOAD_MODULE__(moduleName) 接口(由 MicroApp 补丁注入)
  • 可选:@babel/parser@babel/traverse(用于组件提取,未安装时自动降级)

方案一:一键集成(推荐)

在 webpack 配置的开发分支中调用 applyToWebpack。缺必需配置时仅跳过 HMR、打 warning,不抛错,工程照常构建。

// project.js 或 webpack.config.js
if (process.env.NODE_ENV !== 'production') {
  const microappHmr = require('microapp-hmr');

  microappHmr.applyToWebpack(webpackConfig, webpack, {
    // 可选:重载前清除的全局变量
    clearGlobalsBeforeReload: ['Konva'],

    // 可选:未解析到模块名时的行为
    whenNoModuleNames: 'reload-page', // 'reload-page' 或 'none'
  });
}

什么时候需要提供 isModuleEntry / isPageEntry

  • 默认(不传):按 @hi 约定识别 m/p/
  • 自定义前缀时:请同时提供两者,或改用 moduleEntryPrefixes + pageEntryPrefixes

设计上要求二者成对出现,是为了保证以下链路一致:

  • 模块入口:注入 HMR runtime,负责连接 EventSource、触发子应用重载
  • 页面入口:注入 MicroApp 补丁,挂载 __HMR_RELOAD_MODULE__ 函数;该函数只有在页面里执行,宿主/SystemJS 才会先于子应用加载,MicroApp 才能被正确 patch

方案二:手动配置

如需更细粒度的控制,可分别配置各个组件:

// Loader:组件提取
webpackConfig.module
  .rule('hmr-component-extractor')
  .test(/\.(jsx?|tsx?)$/)
  .exclude.add(/node_modules/)
  .end()
  .use('hmr-loader')
  .loader(microappHmr.getLoaderPath())
  .end();

// Plugin:广播文件变更
webpackConfig
  .plugin('hmr-file-change')
  .use(microappHmr.HMRFileChangePlugin, [{
    logLevel: 'none',
    historyLimit: 10
  }]);

// 运行时注入(仅对模块入口 prepend runtime)
const isModuleEntry = (name) => name && String(name).startsWith('m/');
entryStore.forEach((entry, entryName) => {
  if (isModuleEntry(entryName)) {
    entry.prepend(microappHmr.getRuntimePath());
  }
});

与 @hi/cli 集成

当与 @hi/cli 一起使用时,建议显式传入与你工程一致的 webpack 实例(支持 webpack4 / webpack5)。在混合依赖环境中直接 require('webpack') 可能拿到错误版本,导致 HMR 失效。下面示例优先拿项目 webpack,不匹配时回退到 @hi/cli-core 路径解析:

// project.js
const { dirname } = require('path');

function getWebpackForProject() {
  try {
    const wp = require('webpack');
    if (wp.version && (/^4\./.test(wp.version) || /^5\./.test(wp.version))) return wp;
  } catch {}

  const cliCoreDir = dirname(require.resolve('@hi/cli-core/package.json'));
  return require(require.resolve('webpack', { paths: [cliCoreDir] }));
}

const webpack = getWebpackForProject();

function buildConfig(webpackConfig) {
  if (process.env.NODE_ENV !== 'production') {
    const microappHmr = require('microapp-hmr');
    microappHmr.applyToWebpack(webpackConfig, webpack, {
      // 可省略:默认已按 @hi 的 m/p 识别
      isModuleEntry: (name) => name && String(name).startsWith('m/'),
      isPageEntry: (name) => name && String(name).startsWith('p/'),
    });
  }
}

开发命令配置

在加载 @hi/cli-core 之前先加载包内 enhance-dev-server(对 @hi/cli 的 build 中间件做 HMR 补丁),再执行 cli。强烈建议单独定义开发专用命令,不要覆盖生产 build

{
  "scripts": {
    "dev:hmr": "microapp-hmr-dev-build",
    "build:hmr": "microapp-hmr-dev-build",
    "build": "hicli"
  }
}

无需任何参数;enhance-dev-server、prepend 脚本等均在包内。microapp-hmr-dev-build 仅适用于开发态 HMR,不应直接绑定到生产 build,避免线上构建意外引入 HMR 中间件。


⚙️ 配置参考

客户端配置

在 HTML 中通过 window.__MICROAPP_HMR_CONFIG__ 配置(可覆盖编译期注入的值):

<script>
  window.__MICROAPP_HMR_CONFIG__ = {
    whenNoModuleNames: 'reload-page',
    hmrPath: '/__webpack_hmr',
    hmrOrigin: 'http://127.0.0.1:9601',
    logLevel: 'detailed',
    changeAnalysisExpanded: true,
  };
</script>

applyToWebpack 选项

| 选项 | 说明 | 默认值 | |------|------|--------| | isModuleEntry | 识别子应用/模块入口(与 isPageEntry 成对传入) | 默认按 m/ | | isPageEntry | 识别页面入口(与 isModuleEntry 成对传入) | 默认按 p/ | | moduleEntryPrefixes | 显式模块 entry 前缀数组(与 pageEntryPrefixes 成对传入) | ["m"] | | pageEntryPrefixes | 显式页面 entry 前缀数组(与 moduleEntryPrefixes 成对传入) | ["p"] | | moduleDir | 模块目录名(用于模块名路径解析) | "modules" | | pageDir | 页面目录名(用于兼容检查) | "pages" | | warnIfEntryDirsMissing | 目录不存在时告警(不阻塞) | false | | publicPath | 资源公共路径 | 从 webpack 推导 | | hmrOrigin | HMR 源地址 | 与 publicPath 同源 | | hmrPath | EventSource 路径 | "/__webpack_hmr" | | whenNoModuleNames | 未解析到模块名时的行为 | "reload-page" | | showHmrPanel | 是否显示 HMR 面板 | true | | pluginLogLevel | Node 侧日志级别(含 broker 调试) | "none" | | runtimeLogLevel | 浏览器侧日志级别 | "detailed" | | historyLimit | 面板历史记录条数 | 10 | | clearGlobalsBeforeReload | 重载前清除的全局变量 | null |

环境变量配置

通过 .env 进行差异化配置,避免提交到代码仓库:

# 插件日志级别(同时控制 broker 调试日志)
MICROAPP_HMR_PLUGIN_LOG_LEVEL=none

# 运行时日志级别
MICROAPP_HMR_RUNTIME_LOG_LEVEL=detailed

# 是否显示 HMR 面板
MICROAPP_HMR_SHOW_PANEL=true

# 历史记录条数
MICROAPP_HMR_HISTORY_LIMIT=10

# 多工程 Broker:仅配置端口即可,host/协议沿用当前 dev 地址
MICROAPP_HMR_BROKER_PORT=9601

# 可选:完整 Broker 地址(优先级高于 BROKER_PORT)
# MICROAPP_HMR_BROKER_ORIGIN=http://127.0.0.1:9601
# MICROAPP_HMR_BROKER_PATH=/__microapp_hmr_publish__
# MICROAPP_HMR_BROKER_TOKEN=dev-only-secret

优先级:显式传入选项 > 环境变量 > 默认值


🔄 多工程场景

当一个 devServer 服务多个仅构建工程时,通过 Broker 模式实现统一热更新。

架构示意

┌─────────────────┐      ┌─────────────────┐
│   DevServer 工程  │      │   仅构建工程 A   │
│   (Broker)       │      │                 │
│  ┌─────────────┐ │      │  ┌─────────────┐ │
│  │ 构建 + 服务  │ │      │  │    构建     │ │
│  └─────────────┘ │      │  └─────────────┘ │
│        ↑         │      │        ↓         │
└────────┼─────────┘      └────────┼─────────┘
         │ 广播事件                 │ POST
         │                         │ 变更事件
    ┌────┴─────────────────────────┴────┐
    │         Broker 服务                │
    │  /__webpack_hmr (SSE)              │
    │  /__microapp_hmr_publish__ (API)   │
    └────────────────────────────────────┘

配置步骤

  1. DevServer 工程:正常集成 applyToWebpack 并运行 microapp-hmr-dev-build。devServer 会自动挂载 /__webpack_hmr/__microapp_hmr_publish__,无需额外配置 Broker。

  2. 仅构建工程:同样集成 applyToWebpack,以 watch 方式运行 microapp-hmr-dev-build,在 .env 中配置 同一 Broker 端口

MICROAPP_HMR_BROKER_PORT=9601

浏览器只需连接一台 devServer,即可接收所有工程的变更事件。只要有一个工程开启 devServer,其它 N 个工程配置相同端口并跑 microapp-hmr-dev-build,就都具备 HMR 能力。

多工程下的缓存与重载

仅构建工程的子应用可能由不同端口提供(如 http://127.0.0.1:9602/m/xxx/index.js)。重载时会优先使用 instance.state.module.entry 作为 SystemJS 缓存清理的 URL,确保清理的是实际加载的模块地址,避免「更新后仍显示旧内容」;若框架未暴露该字段,则回退到 moduleEntryUrlTemplate 推导。


🎯 设计决策

为什么是「模块级」而非「组件级」?

当前微前端架构下,整包重载是最优折衷

| 架构约束 | 说明 | |---------|------| | 宿主与子应用分离 | 页面入口是宿主 bundle,子应用为独立 entry,无静态依赖图 | | 事件接收方 | EventSource 由模块 entry 建立,收到事件的是已加载模块的上下文 | | 模块名解析 | 默认按 @hi/cli-corem/ 前缀识别;也可用 moduleEntryPrefixes 显式覆盖 |

变更处理策略

  • 有 moduleNames(变更在模块目录下,路径匹配上述前缀) → 调用 __HMR_RELOAD_MODULE__(name),只重载对应子应用,不刷新整页

  • 无 moduleNames(变更在 pages/ 或其它)** → 宿主代码已变,仅重载子应用无效,必须整页刷新
    → 刷新前将变更文件写入 sessionStorage,刷新后控制台打印触发文件。

未采用的方案

  • ❌ 页面变更时只重载当前页上的顶层 MicroApp:无法更新宿主代码
  • ❌ 插件推导 page → 所用模块映射:无静态可解析映射,不可靠
  • ❌ 依赖页面 entry 自带的 webpack HMR:行为不统一

🔍 常见问题排查

"Several Konva instances detected" 警告

原因:HMR 重载时新旧 Konva 实例共存
解决:配置 clearGlobalsBeforeReload: ["Konva"]

"HMR_RELOAD_MODULE is not a function"

原因:@hi/core 未加载或 monkey-patch 未应用
检查:确保 monkey-patch-microapp.js 已注入到页面入口;控制台应出现 "[HMR] MicroApp 补丁已应用"

EventSource 连接异常

原因hmrOrigin 与 devServer 实际地址不一致
检查:确保 hmrOriginpublicPath 与 devServer 端口一致(如 http://127.0.0.1:9601

构建失败为何不触发重载?

原因:编译有错误时(ESLint/TS 报错),新 bundle 不可用,不应 reload
行为:插件检测到 stats.hasErrors() 时,广播 hmr-build-failed 事件,面板显示红标

如何临时关闭 HMR?

在控制台执行:window.__HMR_STOP__(),会恢复 document.head.appendChild、关闭 EventSource 并清理状态

多工程下「仅构建工程」更新后仍显示旧内容?

原因:SystemJS 缓存 key 与真实模块 URL 不一致(例如用错了端口)
解决:当前实现会优先使用 instance.state.module.entry 清理缓存;若框架未暴露该字段,请确保 moduleEntryUrlTemplate 与实际产出路径一致

TypeScript 报错:Property '__COMPONENT_MAP__' / '__MODULE_ID_MAP__' does not exist on type 'Window'

原因:HMR loader 会在运行时往 window 上挂这些属性,TypeScript 需要加载 microapp-hmrglobal.d.ts 才能通过类型检查。

推荐做法(二选一):

  1. 项目根下已有全局声明文件(如 typings.d.tsenv.d.ts):在其中追加一行即可,无需新建文件。

    /// <reference types="microapp-hmr/global.d.ts" />
  2. 没有现成全局声明文件:在 tsconfig.json 里用 include 显式包含包内的类型声明,不影响 @types/* 的自动发现。

    {
      "compilerOptions": { ... },
      "include": ["**/*", "node_modules/microapp-hmr/global.d.ts"]
    }

不推荐:根目录单独新建 microapp-hmr.d.ts(多一个零散文件);或设置 compilerOptions.types: ["microapp-hmr", ...](会变成白名单模式,需手动维护所有 @types/*)。


📦 构建与发布

# 安装依赖
npm install

# 构建
npm run build   # Rollup 编译:生成 dist/ 和 runtime.js

# 测试
npm test

输出:

  • dist/:Rollup 编译的 CJS/ESM 模块
  • dist/runtime.js:运行时文件(IIFE,可直接 prepend 注入)

模块导出

// 主入口
require('microapp-hmr') → {
  DEFAULT_OPTIONS,
  APPLY_WEBPACK_DEFAULTS,
  getLoaderPath,
  getRuntimePath,
  HMRFileChangePlugin,
  applyToWebpack,
  // ...
}

// 单项导入
require('microapp-hmr/loader')    // Webpack Loader
require('microapp-hmr/plugin')    // HMRFileChangePlugin
require('microapp-hmr/runtime')   // Runtime 脚本路径

📄 License

MIT © 2026


🙏 致谢

感谢所有为微前端架构贡献智慧的开发者们。microapp-hmr 站在巨人的肩膀上,致力于让微前端开发体验更美好。