Angular路由优化:避免路由切换时两组件同时显示
Angular路由动画:等待离开动画完成后再执行进入动画
在使用Angular的router-outlet时,为组件配置:enter和:leave动画后,路由切换过程中两个组件会同时显示,导致容器尺寸突兀变化。以下是两种可行的解决方法:
方法一:使用动画序列(sequence)全局控制路由动画
通过Angular动画的sequence函数按顺序执行离开、进入动画,同时设置离开组件为绝对定位避免布局冲突:
- 定义全局路由动画:
import { animate, sequence, style, transition, trigger, query } from '@angular/animations'; export const routeAnimations = trigger('routeAnimations', [ transition('* <=> *', [ sequence([ // 固定容器定位,避免离开组件影响布局 style({ position: 'relative' }), // 配置离开组件的定位与初始样式 query(':leave', [ style({ position: 'absolute', top: 0, left: 0, width: '100%', opacity: 1 }) ], { optional: true }), // 执行离开动画 query(':leave', animate('300ms ease-out', style({ opacity: 0 })), { optional: true }), // 配置进入组件的初始样式 query(':enter', [ style({ opacity: 0 }) ], { optional: true }), // 执行进入动画 query(':enter', animate('300ms ease-in', style({ opacity: 1 })), { optional: true }) ]) ]) ]);
- 在根组件模板中应用动画:
<div [@routeAnimations]="prepareRoute(outlet)"> <router-outlet #outlet="outlet"></router-outlet> </div>
- 根组件类中添加路由状态判断方法:
import { RouterOutlet } from '@angular/router'; // ... prepareRoute(outlet: RouterOutlet) { return outlet.isActivated ? outlet.activatedRouteData['animation'] || '' : ''; }
这种方式无需修改单个组件,全局统一控制动画执行顺序,同时通过绝对定位让离开组件不占据容器空间,避免尺寸突变。
方法二:通过路由守卫(CanDeactivate)控制导航时机
针对特定组件需要精细控制的场景,可通过路由守卫等待离开动画完成后再允许路由切换:
- 在离开组件中监听动画完成事件:
import { Component, Output, EventEmitter } from '@angular/core'; import { animate, style, transition, trigger } from '@angular/animations'; @Component({ selector: 'app-target-component', animations: [ trigger('leaveAnim', [ transition(':leave', [ style({ opacity: 1 }), animate('300ms ease-out', style({ opacity: 0 })) ]) ]), ], template: `<div [@leaveAnim] (@leaveAnim.done)="onLeaveDone()">...</div>` }) export class TargetComponent { @Output() leaveCompleted = new EventEmitter<void>(); onLeaveDone() { this.leaveCompleted.emit(); } }
- 创建
CanDeactivate守卫:
import { Injectable } from '@angular/core'; import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Observable } from 'rxjs'; export interface CanDeactivateComponent { leaveCompleted: Observable<void>; } @Injectable({ providedIn: 'root' }) export class LeaveAnimGuard implements CanDeactivate<CanDeactivateComponent> { canDeactivate( component: CanDeactivateComponent, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot ): Observable<void> { return component.leaveCompleted; } }
- 在路由配置中添加守卫:
import { Routes } from '@angular/router'; import { TargetComponent } from './target.component'; import { LeaveAnimGuard } from './leave-anim.guard'; const routes: Routes = [ { path: 'target', component: TargetComponent, canDeactivate: [LeaveAnimGuard] } ];
这种方式适合单独控制特定组件的动画时机,但需要为目标组件添加事件监听和守卫配置。
内容的提问来源于stack exchange,提问作者Michał B
相关产品推荐
相关产品推荐

