Angular热替换(cli & webpack)
deepthan opened this issue · 0 comments
deepthan commented
webpack体系原生支持热替换功能,设置也非常简单,只须在webpack-dev-server命令下添加--hot参数:
webpack-dev-server --port=4200 --hot
加上这个hot参数会添加 HotModuleReplacementPlugin 插件到webapck配置里,并启动热替换功能。
如果你用的是 Angular-CLI 搭建,则添加 --hmr 即可开发服务器端的热替换功能:
ng serve --hmr
但是仅仅在开发服务器端开启热替换还不够,Angular 项目仍需要添加热替换处理逻辑,修改 Angular 的启动命令如下如下:
declare var module: any;
if(module.hot) { // 如果webpack启用了热替换功能
// 接受模块更新的事件,同时阻止这个事件继续冒泡
// 参考这文章:https://github.com/webpack/docs/wiki/hot-module-replacement-with-webpack
module.hot.accept();
}
platformBrowserDynamic().bootstrapModule(AppModule)
这时候修改组件代码后,页面不再刷新了,而是把整颗组件树重新渲染,替换DOM节点。
但这样随之而来的一个问题是,在组件里保存的一些数据状态也会丢失,这样的体验显然不好。一个解决办法将组件的数据状态抽取出来外界管理,在热更新渲染前保存数据,渲染后还原数据,示例代码如下:
if(module.hot) {
module.hot.accept();
restoreState(module.hot.data.state); // 还原数据状态
// 模块销毁前触发
module.hot.dispose(() => {
module.hot.data.state = getState(); // 保存数据状态
});
}
webpack的热更新功能打开后,module.hot.data 是一个对象,可以用来在旧模块和新模块之间传递数据
这种有点类似于Redux的做法,Angular世界里其实也有这样工具库,如ngrx。
还有一个问题就是,重新渲染的过程中,一些老的样式不会被清理,导致header里的style标签堆积很多,所以在在重新渲染之前可先清理原来的style标签,示例代码如下:
if(module.hot) {
module.hot.accept();
module.hot.dispose(() => {
let styles = document.head.querySelectorAll('style');
for(let i = 0, len = styles.length; i++; i < len) {
if(styles[i].innerText.indexOf('_ng') >= 0) {
styles[i].remove();
styles[i] = null;
}
}
});
}
热替换功能不依赖ngrx。
热替换后的组件内数据会丢失,用ngrx来管理组件数据可以避免这个问题。