Skip to content

Commit 1549950

Browse files
authored
Merge pull request #19 from worktile/why520crazy/fix-cancel-bootstrap-when-next-change
fix cancel bootstrap when next change
2 parents bd1cd1b + 7f9e56f commit 1549950

File tree

2 files changed

+142
-21
lines changed

2 files changed

+142
-21
lines changed

packages/planet/src/application/planet-application-loader.spec.ts

Lines changed: 119 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const app1 = {
1818
routerPathPrefix: '/app1',
1919
hostClass: 'app1-host',
2020
preload: false,
21-
switchMode: SwitchModes.coexist,
21+
switchMode: SwitchModes.default,
2222
resourcePathPrefix: '/static/app1',
2323
styles: ['styles/main.css'],
2424
scripts: ['vendor.js', 'main.js'],
@@ -35,7 +35,7 @@ const app2 = {
3535
selector: 'app2-root-container',
3636
routerPathPrefix: '/app2',
3737
hostClass: 'app2-host',
38-
preload: false,
38+
preload: true,
3939
switchMode: SwitchModes.coexist,
4040
resourcePathPrefix: '/static/app2',
4141
styles: ['styles/main.css'],
@@ -69,6 +69,11 @@ describe('PlanetApplicationLoader', () => {
6969

7070
planetApplicationService.register(app1);
7171
planetApplicationService.register(app2);
72+
73+
// 创建宿主容器
74+
const hostContainer = document.createElement('DIV');
75+
hostContainer.classList.add('host-selector');
76+
document.body.appendChild(hostContainer);
7277
});
7378

7479
afterEach(() => {
@@ -80,11 +85,6 @@ describe('PlanetApplicationLoader', () => {
8085
const assetsLoaderSpy = spyOn(assetsLoader, 'loadAppAssets');
8186
assetsLoaderSpy.and.returnValue(loadAppAssets$);
8287

83-
// 创建宿主容器
84-
const hostContainer = document.createElement('DIV');
85-
hostContainer.classList.add('host-selector');
86-
document.body.appendChild(hostContainer);
87-
8888
const planetAppRef = mockApplicationRef(app1.name);
8989
const bootstrapSpy = spyOn(planetAppRef, 'bootstrap');
9090

@@ -108,6 +108,7 @@ describe('PlanetApplicationLoader', () => {
108108
expect(bootstrapSpy).toHaveBeenCalled();
109109

110110
expect(appStatusChangeSpy).toHaveBeenCalledTimes(4);
111+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app1, status: ApplicationStatus.bootstrapping });
111112
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app1, status: ApplicationStatus.bootstrapped });
112113

113114
// 判断是否在宿主元素中创建了应用根节点
@@ -117,7 +118,7 @@ describe('PlanetApplicationLoader', () => {
117118
tick();
118119
}));
119120

120-
it(`should cancel load app1 which has not loaded when next route (app2) change`, fakeAsync(() => {
121+
it(`should cancel load app1 which assets has not loaded when next route (app2) change`, fakeAsync(() => {
121122
const loadApp1Assets$ = new Subject();
122123
const loadApp2Assets$ = new Subject();
123124

@@ -153,6 +154,7 @@ describe('PlanetApplicationLoader', () => {
153154
ngZone.onStable.next();
154155

155156
expect(appStatusChangeSpy).toHaveBeenCalledTimes(5);
157+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app2, status: ApplicationStatus.bootstrapping });
156158
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app2, status: ApplicationStatus.bootstrapped });
157159

158160
expect(app1BootstrapSpy).not.toHaveBeenCalled();
@@ -161,5 +163,113 @@ describe('PlanetApplicationLoader', () => {
161163
tick();
162164
}));
163165

164-
it(`should preload apps`, () => {});
166+
it(`should cancel load app1 which has not bootstrapped when next route (app2) change`, fakeAsync(() => {
167+
const loadApp1Assets$ = new Subject();
168+
const loadApp2Assets$ = new Subject();
169+
170+
const planetApp1Ref = mockApplicationRef(app1.name);
171+
const app1BootstrapSpy = spyOn(planetApp1Ref, 'bootstrap');
172+
173+
const planetApp2Ref = mockApplicationRef(app2.name);
174+
const app2BootstrapSpy = spyOn(planetApp2Ref, 'bootstrap');
175+
176+
const assetsLoaderSpy = spyOn(assetsLoader, 'loadAppAssets');
177+
assetsLoaderSpy.and.returnValues(loadApp1Assets$, loadApp2Assets$);
178+
179+
const appStatusChangeSpy = jasmine.createSpy('app status change spy');
180+
planetApplicationLoader.appStatusChange.subscribe(appStatusChangeSpy);
181+
182+
expect(appStatusChangeSpy).not.toHaveBeenCalled();
183+
planetApplicationLoader.reroute({ url: '/app1' });
184+
expect(appStatusChangeSpy).toHaveBeenCalled();
185+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app1, status: ApplicationStatus.assetsLoading });
186+
187+
loadApp1Assets$.next();
188+
loadApp1Assets$.complete();
189+
190+
expect(appStatusChangeSpy).toHaveBeenCalledTimes(2);
191+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app1, status: ApplicationStatus.assetsLoaded });
192+
193+
planetApplicationLoader.reroute({ url: '/app2' });
194+
expect(appStatusChangeSpy).toHaveBeenCalledTimes(3);
195+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app2, status: ApplicationStatus.assetsLoading });
196+
197+
loadApp2Assets$.next();
198+
loadApp2Assets$.complete();
199+
200+
expect(appStatusChangeSpy).toHaveBeenCalledTimes(4);
201+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app2, status: ApplicationStatus.assetsLoaded });
202+
203+
ngZone.onStable.next();
204+
205+
expect(appStatusChangeSpy).toHaveBeenCalledTimes(6);
206+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app2, status: ApplicationStatus.bootstrapping });
207+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app2, status: ApplicationStatus.bootstrapped });
208+
209+
expect(app1BootstrapSpy).not.toHaveBeenCalled();
210+
expect(app2BootstrapSpy).toHaveBeenCalled();
211+
212+
tick();
213+
}));
214+
215+
describe('preload', () => {
216+
it(`should preload load app2 when after loaded app1`, fakeAsync(() => {
217+
const loadApp1Assets$ = new Subject();
218+
const loadApp2Assets$ = new Subject();
219+
220+
const planetApp1Ref = mockApplicationRef(app1.name);
221+
const app1BootstrapSpy = spyOn(planetApp1Ref, 'bootstrap');
222+
223+
const planetApp2Ref = mockApplicationRef(app2.name);
224+
const app2BootstrapSpy = spyOn(planetApp2Ref, 'bootstrap');
225+
226+
const assetsLoaderSpy = spyOn(assetsLoader, 'loadAppAssets');
227+
assetsLoaderSpy.and.returnValues(loadApp1Assets$, loadApp2Assets$);
228+
229+
const appStatusChangeSpy = jasmine.createSpy('app status change spy');
230+
planetApplicationLoader.appStatusChange.subscribe(appStatusChangeSpy);
231+
232+
expect(appStatusChangeSpy).not.toHaveBeenCalled();
233+
planetApplicationLoader.reroute({ url: '/app1' });
234+
expect(appStatusChangeSpy).toHaveBeenCalled();
235+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app1, status: ApplicationStatus.assetsLoading });
236+
237+
loadApp1Assets$.next();
238+
loadApp1Assets$.complete();
239+
240+
expect(appStatusChangeSpy).toHaveBeenCalledTimes(2);
241+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app1, status: ApplicationStatus.assetsLoaded });
242+
243+
ngZone.onStable.next();
244+
245+
expect(appStatusChangeSpy).toHaveBeenCalledTimes(4);
246+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app1, status: ApplicationStatus.bootstrapping });
247+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app1, status: ApplicationStatus.bootstrapped });
248+
249+
expect(app1BootstrapSpy).toHaveBeenCalled();
250+
251+
tick(300);
252+
expect(app2BootstrapSpy).not.toHaveBeenCalled();
253+
254+
// 已经开始加载 App2 静态资源
255+
expect(appStatusChangeSpy).toHaveBeenCalledTimes(5);
256+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app2, status: ApplicationStatus.assetsLoading });
257+
258+
// App2 静态资源加载完毕
259+
loadApp2Assets$.next();
260+
loadApp2Assets$.complete();
261+
262+
expect(appStatusChangeSpy).toHaveBeenCalledTimes(6);
263+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app2, status: ApplicationStatus.assetsLoaded });
264+
265+
// onStable 开始启动应用
266+
ngZone.onStable.next();
267+
expect(appStatusChangeSpy).toHaveBeenCalledTimes(8);
268+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app2, status: ApplicationStatus.bootstrapping });
269+
expect(appStatusChangeSpy).toHaveBeenCalledWith({ app: app2, status: ApplicationStatus.bootstrapped });
270+
expect(app2BootstrapSpy).toHaveBeenCalled();
271+
}));
272+
273+
it(`should preload app`, () => {});
274+
});
165275
});

packages/planet/src/application/planet-application-loader.ts

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export enum ApplicationStatus {
2323
export class PlanetApplicationLoader {
2424
private firstLoad = true;
2525

26+
private startRouteChangeEvent: PlanetRouterEvent;
27+
2628
private options: PlanetOptions;
2729

2830
private inProgressAppAssetsLoads = new Map<string, Observable<PlanetApplication>>();
@@ -90,6 +92,7 @@ export class PlanetApplicationLoader {
9092
.pipe(
9193
switchMap(event => {
9294
this.loadingDone = false;
95+
this.startRouteChangeEvent = event;
9396
const shouldLoadApps = this.planetApplicationService.getAppsByMatchedUrl(event.url);
9497
const shouldUnloadApps = this.getUnloadApps(shouldLoadApps);
9598
this.unloadApps(shouldUnloadApps, event);
@@ -102,7 +105,7 @@ export class PlanetApplicationLoader {
102105
if (shouldLoadApps && shouldLoadApps.length > 0) {
103106
const loadApps$ = shouldLoadApps.map(app => {
104107
const appStatus = this.appsStatus.get(app);
105-
if (!appStatus) {
108+
if (!appStatus || appStatus === ApplicationStatus.assetsLoading) {
106109
return this.startLoadAppAssets(app);
107110
} else {
108111
return of(app);
@@ -131,17 +134,20 @@ export class PlanetApplicationLoader {
131134

132135
// 切换到应用后会有闪烁现象,所以使用 onStable 后启动应用
133136
this.ngZone.onStable.pipe(take(1)).subscribe(() => {
134-
this.ngZone.runOutsideAngular(() => {
135-
shouldShowApps.forEach(app => {
136-
this.showApp(app);
137-
const appRef = getPlanetApplicationRef(app.name);
138-
appRef.onRouteChange(eventAndApps.event);
139-
});
140-
141-
shouldBootstrapApps.forEach(app => {
142-
this.bootstrapApp(app);
137+
// 此处判断是因为如果静态资源加载完毕还未启动被取消,还是会启动之前的应用,虽然可能性比较小,但是无法排除这种可能性,所以只有当 Event 是最后一个才会启动
138+
if (this.startRouteChangeEvent === eventAndApps.event) {
139+
this.ngZone.runOutsideAngular(() => {
140+
shouldShowApps.forEach(app => {
141+
this.showApp(app);
142+
const appRef = getPlanetApplicationRef(app.name);
143+
appRef.onRouteChange(eventAndApps.event);
144+
});
145+
146+
shouldBootstrapApps.forEach(app => {
147+
this.bootstrapApp(app);
148+
});
143149
});
144-
});
150+
}
145151
});
146152

147153
return eventAndApps.apps;
@@ -317,7 +323,12 @@ export class PlanetApplicationLoader {
317323
return this.startLoadAppAssets(app).pipe(
318324
map(() => {
319325
this.ngZone.runOutsideAngular(() => {
320-
this.bootstrapApp(app, 'hidden');
326+
this.ngZone.onStable
327+
.asObservable()
328+
.pipe(take(1))
329+
.subscribe(() => {
330+
this.bootstrapApp(app, 'hidden');
331+
});
321332
});
322333
return getPlanetApplicationRef(app.name);
323334
})

0 commit comments

Comments
 (0)