一、为什么需要HMR?就像给汽车换轮胎不用停车
每次修改代码都要手动刷新浏览器?这就像开车时爆胎非要停下来才能换。HMR(Hot Module Replacement)就是那个让你边开边换轮胎的黑科技。在Angular开发中,保存代码后自动局部更新模块,状态保持、样式即时生效,调试效率直接翻倍。
举个真实场景:你在调试商品详情页的评分组件,传统模式下:
- 改代码 → 2. 按F5 → 3. 重新登录 → 4. 找到测试商品 → 5. 展开评分区域
用了HMR后:改代码 → 看到变化(其他交互状态全保留)
二、Angular HMR配置实战(基于Angular 15+)
2.1 核心配置三件套
先安装关键依赖(技术栈:Angular + Webpack):
npm install @angularclass/hmr --save-dev
然后创建hmr.ts配置文件:
// src/hmr.ts
import { NgModuleRef, ApplicationRef } from '@angular/core';
import { createNewHosts } from '@angularclass/hmr';
export const hmrBootstrap = (
module: any,
bootstrap: () => Promise<NgModuleRef<any>>
) => {
let ngModule: NgModuleRef<any>;
module.hot.accept();
bootstrap().then(mod => {
ngModule = mod;
// 启用HMR状态保持
if (module.hot.data) {
// 这里会保留组件状态
const restoreState = module.hot.data.restoreState;
restoreState && restoreState();
}
});
module.hot.dispose(() => {
const appRef: ApplicationRef = ngModule.injector.get(ApplicationRef);
const elements = appRef.components.map(c => c.location.nativeElement);
// 销毁前保存状态
const restoreState = createNewHosts(elements);
ngModule.destroy();
module.hot.data = { restoreState };
});
};
2.2 修改main.ts启用HMR
// src/main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { hmrBootstrap } from './hmr';
const bootstrap = () => platformBrowserDynamic().bootstrapModule(AppModule);
if (environment.production) {
bootstrap().catch(err => console.error(err));
} else {
// 开发环境启动HMR
if (module['hot']) {
hmrBootstrap(module, bootstrap);
} else {
console.error('HMR未启用,请检查webpack配置');
}
}
2.3 Webpack配置调整(angular.json片段)
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./webpack.hmr.config.js"
}
}
}
三、那些你必须知道的坑与技巧
3.1 状态保持的边界条件
HMR不是魔法,这些情况会失效:
- 修改
@Injectable()服务内部逻辑(需手动处理状态) - 修改路由配置(建议使用
module.hot.invalidate()强制刷新)
3.2 性能优化配置
在webpack.hmr.config.js中添加:
module.exports = {
devServer: {
hot: true,
// 关键:禁用全量重载
liveReload: false,
// 减少检测频率
watchOptions: {
aggregateTimeout: 300,
poll: 1000
}
}
};
四、从原理到实践的全景分析
4.1 技术实现原理
Webpack在背后做了这些事:
- 建立WebSocket连接监听文件变化
- 差异编译改动的模块
- 通过
module.hotAPI进行热替换
4.2 对比其他方案
| 方案 | 优点 | 缺点 |
|---|---|---|
| 传统刷新 | 实现简单 | 丢失所有状态 |
| HMR | 状态保持 | 配置稍复杂 |
| NG Live Reload | 自动刷新 | 仍需重新渲染整个应用 |
4.3 最佳实践场景推荐
- 适合:频繁调整UI样式的开发阶段
- 不适合:修改核心路由配置或全局状态逻辑
五、终极解决方案:HMR+状态管理联调
当使用NgRx时,需要额外处理:
// 在hmr.ts的dispose回调中添加
module.hot.dispose(() => {
const store = ngModule.injector.get(Store);
const state = store.getState();
module.hot.data = { ...module.hot.data, state };
});
这样在重新加载模块时,可以还原Redux状态:
if (module.hot.data?.state) {
store.dispatch({ type: 'HMR_RESTORE', payload: module.hot.data.state });
}
评论