如何使用Angular在HMR期间保持状态 [英] How to preserve state during HMR using Angular

查看:88
本文介绍了如何使用Angular在HMR期间保持状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Angular中,是否有一种方法可以在模块热加载后保留应用程序状态?类似于VueJS中发生的事情:

In Angular, is there a way to preserve application state after a module has been hot reloaded? Similar to what happens in VueJS:

到目前为止,我已经按照以下几篇教程让HMR工作了,但是它所做的只是重新加载应用程序而没有进行实际的页面刷新.是的,更快的满载是.但是仍然没有.

So far I've gotten HMR to work following several tutorials, but all it does is reload the app without doing an actual page refresh. Fasterthe a full load, yes. But still not where it could be.

有没有人真正做到这一点?

Has anyone gotten this to actually work?

PS:它与 https://github.com/beeman相关/tutorial-angular-cli-hmr/issues/4

推荐答案

我在上面尝试了seabass的方法,但使其工作起来有些困难.我确实发现它非常有帮助,并且提供了很多信息.利用他的想法,我能够创建一个新的Angular 6应用程序,并使应用程序状态通过HMR构建得以持久.我在Github上创建了一个项目,这样其他人可以尝试进行开发,因为这是最好的学习方法.阅读代码注释并查看控制台日志,以了解事情发生的顺序及其工作方式.

I tried seabass's approach above, and had some difficulty getting it to work. I did find it to be very helpful and informational though. Using his ideas, I was able to create a new Angular 6 application and get application state to persist through HMR builds. I created a project on Github so others can pull it down if they want to experiment with it, as this is the best way to learn. Read code comments and check the console log to gain understanding about the order in which things happen, and how they work.

https://github.com/ermcgrat/NgStarter

克隆项目,进行npm安装,然后使用" npm run start "运行应用程序.尝试在AppComponent中更改代码,然后查看hmr的工作原理.

Clone the project, do an npm install, and then run application with "npm run start". Try changing code in AppComponent and see how the hmr works.

但是,简而言之,我能够通过创建状态服务并在我的 AppModule hmrBootstrap 中利用它来实现状态持久性.首先,我开始使用Angular CLI团队指定的基本HMR功能:

In short however, I was able to achieve state persistence by creating a state service, and leveraging it in my AppModule and hmrBootstrap. First I started with basic HMR functionality, as specified by the Angular CLI team:

https://github.com/angular/angular-cli/wiki/stories-configure-hmr

这将使HMR正常工作,但不会保持状态不变.我扩展了 hmr.ts 文件,以在处置(卸载)模块时保存状态.在评估新模块时,它将从HMR模块读取此保存的状态,并将其注入到我们的新模块中:

This will get HMR working, but it won't persist state. I extended the hmr.ts file to save our state when the module is disposed (unloaded). When the new module is evaluated it will read this saved state from the HMR module and inject it into our new module:

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);
    }

  });
};

这是我们的AppModule,实现了上面使用的 hmrOnInit hmrOnDestroy 方法.值得注意的是hmrOnInit如何通过状态服务恢复应用程序状态(如果存在).

And here is our AppModule, implementing the hmrOnInit and hmrOnDestroy methods used above. Of note is how the hmrOnInit is restoring application state (if exists) through the state service.

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();
  }
}

最后是 AppStateService .唯一特别棘手的事情是,我们基本上以两种形式维护应用程序状态.其中之一是用于同步访问的普通对象(对于HMR重建,这是必需的,因为无法保证模块处置中的异步功能在评估新模块之前完成).第二个是我们应用程序状态的可观察版本,以便各个组件可以轻松观察到状态的更改/更新.

And finally the AppStateService. The only thing particularly tricky with this is that we are essentially maintaining the application state in 2 forms. 1 of these is a plain-old-vanilla-object for synchronous access (this is necessary for HMR rebuilds, as async functions in the module dispose can't be guaranteed to finish BEFORE the new module is evaluated). The 2nd is an observable version of our application state, so that various components can easily observe changes/updates to state.

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;
  }

}

最后,任何应用程序组件现在都可以观察此 appState $ 并在其组件代码或模板中使用它.

And finally, any application components can now observe this appState$ and use it within their component code or template.

我还要指出,这种在HMR版本之间维持状态的方法本质上导致了单一事实来源.我认为,像 ngrx 这样的状态库可以完美地集成这样的东西.

I would also like to note that this approach to maintaining state between HMR builds essentially leads to a single source of truth. It is in my opinion that a state library such as ngrx would perfectly integrat with something like this.

这篇关于如何使用Angular在HMR期间保持状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆