You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular路由优化:避免路由切换时两组件同时显示

Angular路由动画:等待离开动画完成后再执行进入动画

在使用Angular的router-outlet时,为组件配置:enter和:leave动画后,路由切换过程中两个组件会同时显示,导致容器尺寸突兀变化。以下是两种可行的解决方法:

方法一:使用动画序列(sequence)全局控制路由动画

通过Angular动画的sequence函数按顺序执行离开、进入动画,同时设置离开组件为绝对定位避免布局冲突:

  1. 定义全局路由动画:
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 })
    ])
  ])
]);
  1. 在根组件模板中应用动画:
<div [@routeAnimations]="prepareRoute(outlet)">
  <router-outlet #outlet="outlet"></router-outlet>
</div>
  1. 根组件类中添加路由状态判断方法:
import { RouterOutlet } from '@angular/router';

// ...
prepareRoute(outlet: RouterOutlet) {
  return outlet.isActivated ? outlet.activatedRouteData['animation'] || '' : '';
}

这种方式无需修改单个组件,全局统一控制动画执行顺序,同时通过绝对定位让离开组件不占据容器空间,避免尺寸突变。

方法二:通过路由守卫(CanDeactivate)控制导航时机

针对特定组件需要精细控制的场景,可通过路由守卫等待离开动画完成后再允许路由切换:

  1. 在离开组件中监听动画完成事件:
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();
  }
}
  1. 创建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;
  }
}
  1. 在路由配置中添加守卫:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 09:47:45