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

Angular17 SSR中init为false时如何初始化主/缩略图Swiper(Swiper11.0.6)

解决Angular路由切换时Swiper缩略图组件异常问题

问题背景

在Angular组件中使用Swiper 11.0.6,当路由切换(含参数如/route/:id)时,主Swiper通过ngZone修复后功能正常,但缩略图Swiper仍表现异常。需要实现主Swiper与缩略图Swiper的关联初始化,确保路由切换时无缝运行。

解决方案代码

修改后的HTML

<div>
    <swiper-container #swiper1 style="--swiper-navigation-color: #fff; --swiper-pagination-color: #fff" class="mySwiper"
        navigation="true" init="false">
        <swiper-slide *ngFor="let slide of slides">
            <img [src]="slide.image" />
        </swiper-slide>
    </swiper-container>

    <swiper-container #swiper2 class="mySwiper2" space-between="10" slides-per-view="6" free-mode="true"
        watch-slides-progress="true" init="false">
        <swiper-slide *ngFor="let slide of slides">
            <img [src]="slide.image" />
        </swiper-slide>
    </swiper-container>
</div>

CSS(无需修改)

swiper-slide {
  text-align: center;
  font-size: 18px;
  background: #fff;
  display: flex;
  justify-content: center;
  align-items: center;
  background-size: cover;
  background-position: center;
  img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
}

.mySwiper {
  height: 600px;
  width: 100%;
}

.mySwiper2 {
  // defines height and width of thumbs swiper
  height: 100px;
  width: 100%;
  box-sizing: border-box;
  padding: 10px 0;
  swiper-slide {
    opacity: 0.6; // this set default opacity to all slides
  }
  .swiper-slide-thumb-active {
    opacity: 1; // this reset the opacity one for the active slide
  }
}

修改后的TS代码

import { CommonModule } from '@angular/common';
import {
  Component,
  OnInit,
  CUSTOM_ELEMENTS_SCHEMA,
  ViewChild,
  ElementRef,
  OnDestroy,
  NgZone,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-swiper-thumbs-vertical-dup-1',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './swiper-thumbs-vertical-dup-1.component.html',
  styleUrl: './swiper-thumbs-vertical-dup-1.component.scss',
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class SwiperThumbsVerticalDup1Component implements OnInit, OnDestroy {
  @ViewChild('swiper1') swiper1!: ElementRef<any>;
  @ViewChild('swiper2') swiper2!: ElementRef<any>;

  slides: any[] = [];
  private routeSub!: Subscription;
  private swiper1Instance: any;
  private swiper2Instance: any;

  constructor(private zone: NgZone, private route: ActivatedRoute) { }

  ngOnInit() {
    this.routeSub = this.route.params.subscribe(params => {
      const id = params['id'];
      if (id == 1) {
        this.slides = [
          { image: 'https://swiperjs.com/demos/images/nature-1.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-2.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-3.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-4.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-5.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-6.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-7.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-8.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-10.jpg' },
        ];
      } else {
        this.slides = [
          { image: 'https://swiperjs.com/demos/images/nature-6.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-7.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-8.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-10.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-1.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-2.jpg' },
          { image: 'https://swiperjs.com/demos/images/nature-3.jpg' },
        ];
      }
      // 路由参数更新后重新初始化Swiper
      this.initSwipers();
    });
  }

  ngOnDestroy() {
    // 清理订阅与Swiper实例,避免内存泄漏
    this.routeSub?.unsubscribe();
    this.destroySwipers();
  }

  private initSwipers() {
    // 先销毁旧实例
    this.destroySwipers();

    this.zone.runOutsideAngular(() => {
      // 初始化缩略图Swiper
      this.swiper2Instance = this.swiper2.nativeElement;
      Object.assign(this.swiper2Instance, {
        spaceBetween: 10,
        breakpoints: {
          100: { slidesPerView: 3 },
          640: { slidesPerView: 5 },
          1024: { slidesPerView: 6 }
        },
        freeMode: true,
        watchSlidesProgress: true
      });
      this.swiper2Instance.initialize();

      // 初始化主Swiper并关联缩略图实例
      this.swiper1Instance = this.swiper1.nativeElement;
      Object.assign(this.swiper1Instance, {
        spaceBetween: 10,
        navigation: true,
        thumbs: { swiper: this.swiper2Instance }
      });
      this.swiper1Instance.initialize();
    });
  }

  private destroySwipers() {
    if (this.swiper1Instance) {
      this.swiper1Instance.destroy(true, true);
      this.swiper1Instance = null;
    }
    if (this.swiper2Instance) {
      this.swiper2Instance.destroy(true, true);
      this.swiper2Instance = null;
    }
  }
}

关键修改说明

  1. 移除类选择器关联:删除HTML中thumbs-swiper=".mySwiper2",改为通过JS直接传递缩略图Swiper实例,避免路由切换后类选择器匹配错误。
  2. 封装初始化与销毁逻辑:
    • initSwipers函数先销毁旧实例,再按「缩略图→主Swiper」的顺序初始化,确保主Swiper能正确关联缩略图实例。
    • destroySwipers函数在组件销毁或重新初始化前清理实例,防止内存泄漏。
  3. 路由参数更新触发初始化:在route.params订阅回调中调用initSwipers,确保Slides更新完成后Swiper重新初始化。
  4. 保留ngZone.runOutsideAngular:避免Swiper的DOM操作触发Angular变更检测,提升性能。

内容的提问来源于stack exchange,提问作者Vijai S

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 10:53:12