Sha*_*ane 11 webpack webpack-hmr angular
在Angular中,有一种方法可以在模块热重新加载后保留应用程序状态吗?与VueJS中发生的情况类似:
到目前为止,我已经让HMR在几个教程之后工作了,但它所做的只是重新加载应用程序而不进行实际的页面刷新.快满了,是的.但仍然不是它可能的地方.
有没有人得到这个实际工作?
PS:它与https://github.com/beeman/tutorial-angular-cli-hmr/issues/4有关
小智 8
我尝试过上面的seabass方法,并且在使用它时遇到了一些困难.我确实发现它非常有用和信息.使用他的想法,我能够创建一个新的Angular 6应用程序,并通过HMR构建获得应用程序状态.我在Github上创建了一个项目,所以如果他们想要试验它,其他人可以把它拉下来,因为这是最好的学习方法.阅读代码注释并检查控制台日志,以了解事情发生的顺序以及它们的工作方式.
https://github.com/ermcgrat/NgStarter
克隆项目,执行npm安装,然后使用" npm run start " 运行应用程序.尝试更改AppComponent中的代码并查看hmr的工作原理.
简而言之,我能够通过创建状态服务来实现状态持久性,并在我的AppModule和hmrBootstrap中利用它.首先,我开始使用Angular CLI团队指定的基本HMR功能:
https://github.com/angular/angular-cli/wiki/stories-configure-hmr
这将使HMR工作,但它不会持续状态.我扩展了hmr.ts文件,以便在处理(卸载)模块时保存我们的状态.评估新模块时,它将从HMR模块读取此保存状态并将其注入我们的新模块:
hmr.ts
export const hmrBootstrap = (module: any, bootstrap: () => Promise<NgModuleRef<any>>) => {
module.hot.accept();
bootstrap().then(mod => {
// Attach a dispose handler. When this module is replaced, we will first run this code before
// evaluating the new module. (eg. running main.ts)
module.hot.dispose(data => {
if (mod.instance.hmrOnDestroy) {
mod.instance.hmrOnDestroy(data);
}
const appRef: ApplicationRef = mod.injector.get(ApplicationRef);
const elements = appRef.components.map(c => c.location.nativeElement);
const makeVisible = createNewHosts(elements);
mod.destroy();
makeVisible();
});
// Does this module have an hmrOnInit method for us to run?
// And is there state data from previous unloaded module to initalize?
let prevData;
if (module.hot.data && module.hot.data.appState) {
prevData = module.hot.data.appState;
}
if (mod.instance.hmrOnInit && prevData) {
mod.instance.hmrOnInit(prevData);
}
});
};
Run Code Online (Sandbox Code Playgroud)
这是我们的AppModule,实现上面使用的hmrOnInit和hmrOnDestroy方法.值得注意的是hmrOnInit如何通过状态服务恢复应用程序状态(如果存在).
app.module.ts
export class AppModule {
constructor(private appRef: ApplicationRef, private stateService: AppStateService) { }
hmrOnInit(prevState: any) {
if (prevState) {
this.stateService.saveAppState(prevState);
// change detection.
this.appRef.tick();
}
}
hmrOnDestroy(data: any) {
// Here we will increment our hmrBuilds counter, and then save our state to
// data (module.hot.data), so that it will be available to the new module.
const hmrBuilds = this.stateService.getHmrBuilds() + 1;
this.stateService.saveHmrBuilds(hmrBuilds);
data.appState = this.stateService.getAppState();
}
}
Run Code Online (Sandbox Code Playgroud)
最后是AppStateService.唯一特别棘手的是我们基本上以两种形式维护应用程序状态.其中一个是用于同步访问的普通旧香草对象(这是HMR重建所必需的,因为在评估新模块之前,无法保证模块配置中的异步功能完成).第二个是我们的应用程序状态的可观察版本,因此各种组件可以轻松地观察状态的更改/更新.
app.state.service.ts
export class AppStateService {
// attach various component states to this object
// We maintain an object for synchronous use by the HMR, and an Observable for use by the application and its templates.
private appState: IAppState = { hmrBuilds: 0 };
private appStateSubject = new BehaviorSubject<IAppState>({ hmrBuilds: 0 });
public appState$: Observable<IAppState> = this.appStateSubject.asObservable();
constructor() { }
public getAppState() {
return this.appState;
}
public getHmrBuilds(): number {
return this.appState.hmrBuilds ? this.appState.hmrBuilds : 0;
}
public saveAppState(newState: IAppState) {
this.appState = newState;
this.appStateSubject.next(newState);
}
public saveHmrBuilds(buildNum: number) {
this.appState.hmrBuilds = buildNum;
}
}
Run Code Online (Sandbox Code Playgroud)
最后,任何应用程序组件现在都可以观察到这个appState $并在其组件代码或模板中使用它.
我还要指出,这种在HMR构建之间维持状态的方法实质上导致了单一的事实来源.在我看来,像ngrx这样的状态库可以完美地集成到这样的东西中.
好问题.经过很多头痛之后,我终于让webpack hmr工作并保持角度状态.我的最终实现使用新的environment.hmr.ts作为在生产中或在测试开发服务器上禁用hmr的方法.我没有实现建议的.hmr文件,而是将该代码留在main.ts中
考虑一下:您在文件中进行更改并保存.Webpack编译,魔术发生,你的Angular应用程序即将被拆除并与新的更改交换.
Webpack调用我们的函数,module['hot']['dispose']然后在OnDestroy之前的应用程序的AppModule类中调用它,让我们有机会保存我们的状态.
然后Webpack加载我们的新应用程序,它启动,并调用OnInitAppModule,传递它module['hot']['data']是我们的状态.
main.ts
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
function bootstrap(AppModule) {
return platformBrowserDynamic().bootstrapModule(AppModule)
.then(MODULE_REF => {
if (environment.hmr) {
if (module['hot']) {
module['hot']['accept']();
if (MODULE_REF.instance['OnInit']) {
if (module['hot']['data']) {
// Calls OnInit on app.module.ts
MODULE_REF.instance['OnInit'](module['hot']['data']);
}
}
if (MODULE_REF.instance['OnStatus']) {
module['hot']['apply']((status) => {
MODULE_REF.instance['OnStatus'](status);
});
}
if (MODULE_REF.instance['OnCheck']) {
module['hot']['check']((err, outdatedModules) => {
MODULE_REF.instance['OnCheck'](err, outdatedModules);
});
}
if (MODULE_REF.instance['OnDecline']) {
module['hot']['decline']((dependencies) => {
MODULE_REF.instance['OnDecline'](dependencies);
});
}
module['hot']['dispose'](store => {
if (MODULE_REF.instance['OnDestroy']) {
// Calls OnDestroy on app.module.ts
MODULE_REF.instance['OnDestroy'](store);
}
MODULE_REF.destroy();
if (MODULE_REF.instance['AfterDestroy']) {
MODULE_REF.instance['AfterDestroy'](store);
}
});
}
}
return MODULE_REF;
});
}
bootstrap(AppModule);
在这里,我们可以将以前应用程序的状态存储module['hot']['data']在作为store参数传入的状态中.这个相同的商店参数被传递到新的应用程序中,OnInit(store)使我们能够维护新应用程序中的任何状态对象.
app.module.ts
export class AppModule {
constructor(private _state: StateService) { }
OnInit(store) {
if (store !== undefined) {
this._state.SetState(store.State);
}
}
OnDestroy(store) {
store.State = this._state;
}
}
这是我的基本国家服务.您可能更喜欢在这里使用ngrx进行状态管理,但我觉得这对我的项目来说太过分了.
state.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable()
export class StateService {
public SideNavIsOpen: BehaviorSubject;
constructor() {
this.SideNavIsOpen = new BehaviorSubject(false);
}
public SetState(_state: StateService) {
this.SideNavIsOpen = _state.SideNavIsOpen;
}
}
| 归档时间: |
|
| 查看次数: |
2301 次 |
| 最近记录: |