问题
I've been working on an application in Angular2/CLI/NGRX and things have been going well until recently. I'm noticing some pretty big spikes in performance especially with consecutive dispatches within the same container.
For example lets say I have the following defined:
public appEnvironment$: Observable<IEnvironment>;
public assessment$: Observable<IAssessment>;
public assessmentComments$: Observable<ICommentActivity[]>;
public assessmentEvidence$: Observable<IEvidenceActivity[]>;
public assessmentIssues$: Observable<IIssueActivity[]>;
public assessmentSurvey$: Observable<ISurvey>;
public assessmentUsers$: Observable<ISystemUser[]>;
public assessmentSelectedNode$: Observable<ISurveyChildNode>;
constructor(private _store: Store<fromDomain.State>, private _route: ActivatedRoute) {
this.appEnvironment$ = _store.select(fromDomain.getEnvironment).share();
this.assessment$ = _store.select(fromDomain.getAssessment).share();
this.assessmentComments$ = _store.select(fromDomain.getAssessmentComments).share();
this.assessmentIssues$ = _store.select(fromDomain.getAssessmentIssues).share();
this.assessmentEvidence$ = _store.select(fromDomain.getAssessmentEvidence).share();
this.assessmentSurvey$ = _store.select(fromDomain.getAssessmentSurvey).share();
this.assessmentUsers$ = _store.select(fromDomain.getAssessmentUsers).share();
this.assessmentSelectedNode$ = _store.select(fromDomain.getAssessmentSelectedNode).share();
this.openAssessmentId = _route.snapshot.params['id'];
this._store.dispatch(new LoadAssessmentAction(this.openAssessmentId));
}
It's also worth noting that the above is all the state selection required to load the child components and their data shared across multiple components (hence the .share()) something like:
<opt-drawer-index
#drawerShow
[leftHeading]="'Survey Info'"
[leftIcon]="'fa-bars'"
[rightHeading]="'Assessment Details'"
[onForceChange]="assessmentSelectedNode$ | async">
<section drawer-left-content>
<opt-assessment-show-survey-info
[appEnvironment]="appEnvironment$ | async"
[assessment]="assessment$ | async"
[assessmentUsers]="assessmentUsers$ | async"></opt-assessment-show-survey-info>
</section>
<section drawer-content>
<opt-assessment-show-content
[appEnvironment]="appEnvironment$ | async"
[assessment]="assessment$ | async"
[assessmentSurvey]="assessmentSurvey$ | async"
(selectedNode)="changeSelectedNode($event)"></opt-assessment-show-content>
</section>
<section drawer-right-content>
<opt-assessment-show-details
[activeNode]="assessmentSelectedNode$ | async"
[appEnvironment]="appEnvironment$ | async"
[assessment]="assessment$ | async"
[assessmentComments]="assessmentComments$ | async"
[assessmentEvidence]="assessmentEvidence$ | async"
[assessmentIssues]="assessmentIssues$ | async"
[assessmentUsers]="assessmentUsers$ | async"></opt-assessment-show-details>
</section>
</opt-drawer-index>
The initial load is great and works well. I have the freeze-state active and no mutations are occurring within the state. All components are utilizing the OnPush strategy as well.
The problem is within the center content component I have an event emitter that talks with the container component. It sends up an object to 'select' and that fires off an action through the dispatcher to update the state with the selected option. The first couple clicks run great and then you start to notice some serious power consumption as you continue to click on different areas throughout the child components. It's almost as if the dispatcher seems to be bogged down.
I've tried a few things like using combineLatest() and other tools to lessen the burden on the updates, however that seemed to make matters worse. Currently the way the application loads data is as follows:
Load Assessment -> After Load Assessment Effect -> Select Assessment -> After Load Selected Assessment Effect
Anyone else run into performance issues? Is it something unrelated to NGRX and the way that I have things setup? I've mainly used the NGRX example app as a reference point for how to lay out my setup.
Edit
Here's a timeline representation of the problem I'm having. It's almost as though the click event is getting exponentially longer?
Edit 2
I am using reselect and here's the effects for the page that is hanging after subsequent clicks:
import {Injectable} from "@angular/core";
// NGRX
import {Actions, Effect} from "@ngrx/effects";
import {Action} from "@ngrx/store";
// Services
import {AssessmentService} from "./assessment.service";
import {SurveyService} from "../survey/survey.service";
import {SystemUserService} from "../system-user/system-user.service";
// Observable and operators
import {Observable} from "rxjs/Observable";
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/concatMap';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/map';
// Misc
import * as assessment from './assessment.actions';
import * as assessmentNav from './navigation/assessments-navigation.actions';
@Injectable()
export class AssessmentEffects {
constructor(private actions$: Actions, private assessmentsService: AssessmentService,
private surveyService: SurveyService, private systemUserService: SystemUserService) { }
@Effect()
effLoadAssessment$: Observable<Action> = this.actions$
.ofType(assessment.ActionTypes.LOAD_ASSESSMENT)
.map((action: assessment.LoadAssessmentAction) => action.payload)
.switchMap(guid => {
return this.assessmentsService.getAssessment(guid)
.map(v => new assessment.LoadAssessmentCompleteAction(v));
});
@Effect()
effAfterLoadAssessment: Observable<Action> = this.actions$
.ofType(assessment.ActionTypes.LOAD_ASSESSMENT_COMPLETE)
.map((action: assessment.LoadAssessmentCompleteAction) => action.payload)
.mergeMap(theAssessment => {
return [
new assessment.LoadAssessmentSurveyAction(theAssessment.surveyID),
new assessmentNav.AssessmentNavAddAction(theAssessment),
new assessment.LoadAssessmentUserAction(theAssessment.instanceOwner_SystemUserID)
];
});
@Effect()
effLoadAssessmentComments$: Observable<Action> = this.actions$
.ofType(assessment.ActionTypes.LOAD_ASSESSMENT_COMMENTS)
.map((action: assessment.LoadAssessmentCommentsAction) => action.payload)
.switchMap(multiRequest => {
return this.assessmentsService
.getAssessmentComments(multiRequest.assessmentId, multiRequest.type, multiRequest.nodeId)
.map(v => new assessment.LoadAssessmentCommentsCompleteAction(v));
});
@Effect()
effAfterSelectedNode$: Observable<Action> = this.actions$
.ofType(assessment.ActionTypes.SELECT_ASSESSMENT_NODE)
.map((action: assessment.SelectedNodeAction) => action.payload)
.mergeMap(theNode => {
return [
new assessment.LoadAssessmentCommentsAction({
type: 'Comments',
nodeId: theNode.id,
assessmentId: theNode.assessmentId
}),
new assessment.LoadAssessmentIssuesAction({
type: 'Issues',
nodeId: theNode.id,
assessmentId: theNode.assessmentId
}),
new assessment.LoadAssessmentEvidenceAction({
type: 'Attachments',
nodeId: theNode.id,
assessmentId: theNode.assessmentId
})
];
});
@Effect()
effLoadAssessmentIssues$: Observable<Action> = this.actions$
.ofType(assessment.ActionTypes.LOAD_ASSESSMENT_ISSUES)
.map((action: assessment.LoadAssessmentIssuesAction) => action.payload)
.switchMap(multiRequest => {
return this.assessmentsService
.getAssessmentIssues(multiRequest.assessmentId, multiRequest.type, multiRequest.nodeId)
.map(v => new assessment.LoadAssessmentIssuesCompleteAction(v));
});
@Effect()
effLoadAssessmentEvidence$: Observable<Action> = this.actions$
.ofType(assessment.ActionTypes.LOAD_ASSESSMENT_EVIDENCE)
.map((action: assessment.LoadAssessmentEvidenceAction) => action.payload)
.switchMap(multiRequest => {
return this.assessmentsService
.getAssessmentEvidence(multiRequest.assessmentId, multiRequest.type, multiRequest.nodeId)
.map(v => new assessment.LoadAssessmentEvidenceCompleteAction(v));
});
@Effect()
effLoadAssessmentUser$: Observable<Action> = this.actions$
.ofType(assessment.ActionTypes.LOAD_ASSESSMENT_USER)
.map((action: assessment.LoadAssessmentUserAction) => action.payload)
.concatMap(guid => {
return this.systemUserService.getSystemUser(guid)
.map(v => new assessment.LoadAssessmentUserCompleteAction(v));
});
@Effect()
effLoadAssessmentSurvey$: Observable<Action> = this.actions$
.ofType(assessment.ActionTypes.LOAD_ASSESSMENT_SURVEY)
.map((action: assessment.LoadAssessmentSurveyAction) => action.payload)
.switchMap(guid => {
return this.surveyService.getSurvey(guid)
.map(v => new assessment.LoadAssessmentSurveyCompleteAction(v));
});
}
回答1:
The slowdown was actually related to the @ngrx/store-devtools
. After I removed the module from the application the speed was phenomenal. We were hoping to use the tooling for snapshotting and replaying state, but I'm not sure we can go down that route anymore with the performance hit.
来源:https://stackoverflow.com/questions/42354606/angular2-ngrx-performance-issues-on-dispatch