Angular多布局切换刷新时主组件闪现问题求助
多布局组件刷新闪现问题解决
问题描述
需要实现多模块布局,现有主根布局和user独立布局:
- 主布局包含跳转至user组件的按钮,点击后主组件消失,显示user组件
- user布局包含跳转至主组件的按钮,点击后user组件消失,显示主组件
通过Service实现了切换逻辑,但刷新/user页面时,主组件会短暂闪现,需要解决该问题。
现有代码实现
AppComponent
@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnDestroy { show: boolean = true; subscription: Subscription; constructor(private showContentService: ShowContentService) { this.subscription = showContentService.contentHome.subscribe(value => { this.show = value; }); } hideContent() { this.show = false; } ngOnDestroy() { // 防止组件销毁时内存泄漏 this.subscription.unsubscribe(); } }
app.component.html
<ng-container *ngIf="show"> <p>Main page</p> <button (click)="hideContent()" routerLink="/user">User</button> </ng-container> <router-outlet></router-outlet>
AppRoutingModule
const routes: Routes = [ { path: 'user', loadChildren: () => import('./user/user.module').then(m => m.UserModule) } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
ShowContentService
@Injectable({ providedIn: 'root' }) export class ShowContentService { // Observable数据源 private showContent = new Subject<boolean>(); // Observable流 contentHome = this.showContent.asObservable(); showContentHome(showContent: boolean) { this.showContent.next(showContent); } }
UserComponent
export class UserComponent { constructor(private showContentService: ShowContentService) { this.showContentService.showContentHome(false); } showContentHome() { this.showContentService.showContentHome(true); } ngOnInit(): void { } }
user.component.html
<p>User page</p> <button (click)="showContentHome()" routerLink="/">Home</button>
UserRoutingModule
const routes: Routes = [ { path: '', component: UserComponent } ]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) export class UserRoutingModule { }
问题根源
刷新/user页面时,AppComponent会先完成初始化,此时show默认值为true,主组件会先渲染显示;直到UserComponent初始化后,才会通过Service将show设为false,这中间的时序差导致了主组件短暂闪现。
解决方法
方法1:通过路由状态直接控制(简单有效)
移除Service依赖,直接在AppComponent中监听路由变化,根据当前路由判断是否显示主组件:
修改AppComponent:
import { Component, OnDestroy } from '@angular/core'; import { Router, NavigationEnd } from '@angular/router'; import { Subscription } from 'rxjs'; import { filter } from 'rxjs/operators'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnDestroy { show: boolean = true; private routerSubscription: Subscription; constructor(private router: Router) { // 初始化时直接根据当前路由设置显示状态 this.show = this.router.url === '/'; // 监听路由导航结束事件,更新显示状态 this.routerSubscription = this.router.events.pipe( filter(event => event instanceof NavigationEnd) ).subscribe((event: NavigationEnd) => { this.show = event.url === '/'; }); } ngOnDestroy() { this.routerSubscription.unsubscribe(); } }
同时删除以下代码:
- AppComponent中ShowContentService的注入和订阅逻辑
- UserComponent中ShowContentService的注入和调用逻辑
方法2:将Service的Subject改为BehaviorSubject
BehaviorSubject会保存最新值,订阅时会立即拿到当前值,避免初始化时的空白等待:
修改ShowContentService:
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class ShowContentService { // 初始化时默认值可根据路由动态设置,这里先设为true,后续在AppComponent中修正 private showContent = new BehaviorSubject<boolean>(true); contentHome = this.showContent.asObservable(); showContentHome(showContent: boolean) { this.showContent.next(showContent); } }
修改AppComponent的构造函数,初始化时根据路由设置初始值:
constructor(private showContentService: ShowContentService, private router: Router) { // 初始化时根据当前路由设置正确的显示状态 const initialShow = this.router.url === '/'; this.showContentService.showContentHome(initialShow); this.subscription = showContentService.contentHome.subscribe(value => { this.show = value; }); }
方法3:使用路由布局组件(官方推荐规范写法)
这是Angular实现多布局的标准方式,不同路由对应不同布局组件,从根源避免闪现问题:
- 创建
HomeLayoutComponent作为主布局:
@Component({ selector: 'app-home-layout', template: ` <p>Main page</p> <button routerLink="/user">User</button> <router-outlet></router-outlet> ` }) export class HomeLayoutComponent {}
- 创建
UserLayoutComponent作为user布局:
@Component({ selector: 'app-user-layout', template: ` <p>User page</p> <button routerLink="/">Home</button> <router-outlet></router-outlet> ` }) export class UserLayoutComponent {}
- 修改AppRoutingModule,指定路由对应的布局组件:
const routes: Routes = [ { path: '', component: HomeLayoutComponent, // 主布局下的子路由,比如首页组件 children: [{ path: '', component: HomeComponent }] }, { path: 'user', component: UserLayoutComponent, loadChildren: () => import('./user/user.module').then(m => m.UserModule) } ];
- 简化AppComponent的template,只保留根路由出口:
<router-outlet></router-outlet>
这种方式下,刷新页面时Angular会直接加载对应路由的布局组件,不会加载其他布局的内容,彻底解决闪现问题。
内容的提问来源于stack exchange,提问作者MarieSpeak
相关产品推荐
相关产品推荐

