Skip to content

Commit

Permalink
fix(app): can't start when url is same, don't reload active app #265669
Browse files Browse the repository at this point in the history
  • Loading branch information
why520crazy committed Sep 25, 2019
1 parent 9d03c02 commit af61eb4
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 46 deletions.
54 changes: 52 additions & 2 deletions packages/planet/src/application/planet-application-loader.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const app1 = {
hostClass: 'app1-host',
preload: false,
switchMode: SwitchModes.default,
resourcePathPrefix: '/static/app1',
resourcePathPrefix: '/static/app1/',
styles: ['styles/main.css'],
scripts: ['vendor.js', 'main.js'],
loadSerial: false,
Expand Down Expand Up @@ -132,6 +132,56 @@ describe('PlanetApplicationLoader', () => {
tick();
}));

it(`should not bootstrap app1 which is active`, fakeAsync(() => {
const loadAppAssets$ = new Subject();
const assetsLoaderSpy = spyOn(assetsLoader, 'loadAppAssets');
assetsLoaderSpy.and.returnValue(loadAppAssets$);

// App state change
const appStatusChangeSpy = jasmine.createSpy('app status change spy');
planetApplicationLoader.appStatusChange.subscribe(appStatusChangeSpy);
expect(appStatusChangeSpy).not.toHaveBeenCalled();

const [bootstrapSpy] = spyPlanetApplicationRef(app1.name);
planetApplicationLoader.reroute({ url: '/app1/dashboard' });
loadAppAssets$.next();
loadAppAssets$.complete();
ngZone.onStable.next();
expect(bootstrapSpy).toHaveBeenCalled();
// 判断是否在宿主元素中创建了应用根节点
expectApp1Element();

expect(appStatusChangeSpy).toHaveBeenCalledTimes(5);
planetApplicationLoader.reroute({ url: '/app1/dashboard2' });
ngZone.onStable.next();
expect(bootstrapSpy).toHaveBeenCalledTimes(1);
expect(appStatusChangeSpy).toHaveBeenCalledTimes(5);
tick();
}));

it(`should start load app1 once when reroute same url: /app1/dashboard`, fakeAsync(() => {
const loadAppAssets$ = new Subject();
const assetsLoaderSpy = spyOn(assetsLoader, 'loadAppAssets');
assetsLoaderSpy.and.returnValue(loadAppAssets$);

// Apps loading start
const appsLoadingStartSpy = jasmine.createSpy('apps loading start spy');
planetApplicationLoader.appsLoadingStart.subscribe(appsLoadingStartSpy);
expect(appsLoadingStartSpy).not.toHaveBeenCalled();

planetApplicationLoader.reroute({ url: '/app1/dashboard' });
expect(appsLoadingStartSpy).toHaveBeenCalledTimes(1);
expect(appsLoadingStartSpy).toHaveBeenCalled();
expect(appsLoadingStartSpy).toHaveBeenCalledWith({
shouldLoadApps: [app1],
shouldUnloadApps: []
});
planetApplicationLoader.reroute({ url: '/app1/dashboard' });
expect(appsLoadingStartSpy).toHaveBeenCalledTimes(1);

tick();
}));

it(`should cancel load app1 which assets has not loaded when next route (app2) change`, fakeAsync(() => {
const loadApp1Assets$ = new Subject();
const loadApp2Assets$ = new Subject();
Expand Down Expand Up @@ -313,7 +363,7 @@ describe('PlanetApplicationLoader', () => {
expect(appStatusChangeSpy).toHaveBeenCalledTimes(2);
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app1, status: ApplicationStatus.loadError });

planetApplicationLoader.reroute({ url: '/app1' });
planetApplicationLoader.reroute({ url: '/app1/hello' });
expect(appStatusChangeSpy).toHaveBeenCalledTimes(3);
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app1, status: ApplicationStatus.assetsLoading });

Expand Down
92 changes: 50 additions & 42 deletions packages/planet/src/application/planet-application-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Injectable, NgZone, ApplicationRef, Injector } from '@angular/core';
import { of, Observable, Subject, forkJoin, throwError } from 'rxjs';
import { AssetsLoader, AssetsLoadResult } from '../assets-loader';
import { PlanetApplication, PlanetRouterEvent, SwitchModes, PlanetOptions } from '../planet.class';
import { switchMap, finalize, share, map, tap, delay, take, filter, catchError } from 'rxjs/operators';
import { getScriptsAndStylesFullPaths, getHTMLElement, coerceArray } from '../helpers';
import { switchMap, finalize, share, map, tap, distinctUntilChanged, take, filter, catchError } from 'rxjs/operators';
import { getHTMLElement, coerceArray } from '../helpers';
import { PlanetApplicationRef, getPlanetApplicationRef, globalPlanet } from './planet-application-ref';
import { PlanetPortalApplication } from './portal-application';
import { PlanetApplicationService } from './planet-application.service';
Expand Down Expand Up @@ -108,6 +108,9 @@ export class PlanetApplicationLoader {
private setupRouteChange() {
this.routeChange$
.pipe(
distinctUntilChanged((x, y) => {
return (x && x.url) === (y && y.url);
}),
// Using switchMap so we cancel executing loading when a new one comes in
switchMap(event => {
// Return new observable use of and catchError,
Expand Down Expand Up @@ -150,39 +153,45 @@ export class PlanetApplicationLoader {
}),
// Bootstrap or show apps
map(apps => {
const shouldBootstrapApps = [];
const shouldShowApps = [];
const shouldBootstrapApps: PlanetApplication[] = [];
const shouldShowApps: PlanetApplication[] = [];
apps.forEach(app => {
const appStatus = this.appsStatus.get(app);
if (appStatus === ApplicationStatus.bootstrapped) {
shouldShowApps.push(app);
} else {
} else if (appStatus === ApplicationStatus.assetsLoaded) {
shouldBootstrapApps.push(app);
} else if (appStatus === ApplicationStatus.active) {
} else {
throw new Error(
`app(${app.name})'s status is ${appStatus}, can't be show or bootstrap`
);
}
});

// 切换到应用后会有闪烁现象,所以使用 onStable 后启动应用
this.ngZone.onStable
.asObservable()
.pipe(take(1))
.subscribe(() => {
// 此处判断是因为如果静态资源加载完毕还未启动被取消,还是会启动之前的应用,虽然可能性比较小,但是无法排除这种可能性,所以只有当 Event 是最后一个才会启动
if (this.startRouteChangeEvent === event) {
this.ngZone.runOutsideAngular(() => {
shouldShowApps.forEach(app => {
this.showApp(app);
const appRef = getPlanetApplicationRef(app.name);
appRef.navigateByUrl(event.url);
this.setAppStatus(app, ApplicationStatus.active);
});

shouldBootstrapApps.forEach(app => {
this.bootstrapApp(app);
this.setAppStatus(app, ApplicationStatus.active);
if (shouldShowApps.length > 0 || shouldBootstrapApps.length > 0) {
// 切换到应用后会有闪烁现象,所以使用 onStable 后启动应用
this.ngZone.onStable
.asObservable()
.pipe(take(1))
.subscribe(() => {
// 此处判断是因为如果静态资源加载完毕还未启动被取消,还是会启动之前的应用,虽然可能性比较小,但是无法排除这种可能性,所以只有当 Event 是最后一个才会启动
if (this.startRouteChangeEvent === event) {
this.ngZone.runOutsideAngular(() => {
shouldShowApps.forEach(app => {
this.showApp(app);
const appRef = getPlanetApplicationRef(app.name);
appRef.navigateByUrl(event.url);
this.setAppStatus(app, ApplicationStatus.active);
});
shouldBootstrapApps.forEach(app => {
this.bootstrapApp(app);
this.setAppStatus(app, ApplicationStatus.active);
});
});
});
}
});
}
});
}

return apps;
}),
Expand Down Expand Up @@ -291,10 +300,7 @@ export class PlanetApplicationLoader {
private getUnloadApps(activeApps: PlanetApplication[]) {
const unloadApps: PlanetApplication[] = [];
this.appsStatus.forEach((value, app) => {
if (
[ApplicationStatus.bootstrapped, ApplicationStatus.active].includes(value) &&
!activeApps.find(item => item.name === app.name)
) {
if (value === ApplicationStatus.active && !activeApps.find(item => item.name === app.name)) {
unloadApps.push(app);
}
});
Expand All @@ -318,19 +324,21 @@ export class PlanetApplicationLoader {
}
});

// 从其他应用切换到主应用的时候会有视图卡顿现象,所以先等主应用渲染完毕后再加载其他应用
// 此处尝试使用 this.ngZone.onStable.pipe(take(1)) 应用之间的切换会出现闪烁
setTimeout(() => {
hideApps.forEach(app => {
const appRef = getPlanetApplicationRef(app.name);
if (appRef) {
appRef.navigateByUrl(event.url);
}
});
destroyApps.forEach(app => {
this.destroyApp(app);
if (hideApps.length > 0 || destroyApps.length > 0) {
// 从其他应用切换到主应用的时候会有视图卡顿现象,所以先等主应用渲染完毕后再加载其他应用
// 此处尝试使用 this.ngZone.onStable.pipe(take(1)) 应用之间的切换会出现闪烁
setTimeout(() => {
hideApps.forEach(app => {
const appRef = getPlanetApplicationRef(app.name);
if (appRef) {
appRef.navigateByUrl(event.url);
}
});
destroyApps.forEach(app => {
this.destroyApp(app);
});
});
});
}
}

private preloadApps(activeApps?: PlanetApplication[]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const app1 = {
routerPathPrefix: '/app1',
hostClass: 'app1-host',
preload: false,
resourcePathPrefix: '/static/app1',
resourcePathPrefix: '/static/app1/',
styles: ['styles/main.css'],
scripts: ['vendor.js', 'main.js'],
loadSerial: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe('PlanetApplicationService', () => {
hostClass: 'app1-host',
preload: false,
switchMode: SwitchModes.coexist,
resourcePathPrefix: '/static/app1',
resourcePathPrefix: '/static/app1/',
styles: ['styles/main.css'],
scripts: ['vendor.js', 'main.js'],
loadSerial: false,
Expand Down

0 comments on commit af61eb4

Please sign in to comment.