首頁
學習紀錄
遊戲心得影視Life書單案件檔案
Side Projects委託作品與二創互動實驗場
Kurau
百百 BLOG
首頁
學習紀錄
遊戲心得影視Life書單案件檔案
Side Projects委託作品與二創互動實驗場
Kurau

Kurau Blog

「隨心而寫,真真假假,都是我」

一個記錄生活、輸出興趣的個人空間。
遊戲、影視、閱讀、學習……每一段體驗都值得留下文字。

頁面導覽

  • 學習紀錄
  • 遊戲心得
  • 影視Life
  • 書單
  • 委託作品與二創
  • Kurau
  • 合作邀請

找到我

歡迎來 Discord 找我聊天!

“曾經發生的事不可能忘記,只是暫時想不起來而已。”-《神隱少女》

© 2026 Kurau All rights reserved

前端框架

Swiper 踩坑

By Kurau·2024-03-14·Updated 2026-05-09·4 分鐘閱讀

Swiper 踩坑紀錄

TL;DR
Swiper.js 在 React / Next.js 上的常見地雷:圖片底部空隙(display: block)、centerMode 露出半張的設定、動態更新 slides 不生效(要 observer)、SSR 相容性(動態 import)。
參考
Stack Overflow — Swiper.js: how to show 2 half slides and 2 full slides

安裝

npm install swiper
bash
import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation, Pagination, Autoplay } from 'swiper/modules';

import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
tsx

CSS 要分別 import,別漏了 module 對應的樣式檔。


踩坑 1:圖片四周留白

圖片預設是 inline 元素,baseline 對齊會留底部空隙(放字母 'g' 的下緣空間)。

.swiper-slide img {
  display: block;       /* 關鍵:消除 inline baseline 空隙 */
  width: 100%;
  height: 100%;
  object-fit: cover;
}
css
一勞永逸
在全域 reset 加 img { display: block; },不只 Swiper,所有圖片都不會再有這問題。

踩坑 2:CenterMode 露出半張

要做「中間一張完整 + 左右各露出半張」效果:

<Swiper
  slidesPerView={2.5}        // ⭐ 關鍵:小數點
  centeredSlides={true}      // 中間對齊
  spaceBetween={20}
  loop={true}
>
  {items.map((item) => (
    <SwiperSlide key={item.id}>{item.content}</SwiperSlide>
  ))}
</Swiper>
tsx

slidesPerView 用 2.5 表示「顯示 2.5 張」 → 中間 1 張完整 + 左右各 0.75 張(會被 viewport 切半)。配 centeredSlides 讓 active 的那張在正中。


踩坑 3:動態更新 slides 不生效

當你的 items 是動態載入(從 API 來)、或會新增/刪除,Swiper 不會自動偵測 DOM 變化:

<Swiper
  observer={true}           // 監看 swiper 自身變化
  observeParents={true}     // 監看 parent 變化(響應式必備)
  observeSlideChildren={true}  // 監看 slide 內子元素變化
>
tsx
加完還是不更新?
試試 useEffect + swiper.update():
const swiperRef = useRef<SwiperType>(null);
useEffect(() => { swiperRef.current?.update(); }, [items]);
<Swiper onSwiper={(s) => (swiperRef.current = s)}>...</Swiper>
tsx

踩坑 4:Next.js SSR 相容性

Swiper 用到 window / document,在 Next.js SSR 階段會炸:

ReferenceError: window is not defined

解法 1:動態 import(推薦)

import dynamic from 'next/dynamic';

const Swiper = dynamic(
  () => import('swiper/react').then((mod) => mod.Swiper),
  { ssr: false }
);
const SwiperSlide = dynamic(
  () => import('swiper/react').then((mod) => mod.SwiperSlide),
  { ssr: false }
);
tsx

代價:輪播 等到 hydration 後才出現,首屏會有空白瞬間。可以加 skeleton 緩解。

解法 2:'use client' directive(App Router)

'use client';

import { Swiper, SwiperSlide } from 'swiper/react';
// ...
tsx

App Router 推薦這個,比 dynamic import 簡單,且 client component 在 streaming 模式下不會 block hydration。


踩坑 5:Pagination / Navigation 樣式覆寫

預設箭頭跟分頁點 不一定符合設計,要客製:

/* 改箭頭顏色 */
.swiper-button-next,
.swiper-button-prev {
  color: var(--color-accent);
}

/* 改分頁點 */
.swiper-pagination-bullet {
  background: var(--color-text-muted);
  opacity: 0.5;
}
.swiper-pagination-bullet-active {
  background: var(--color-accent);
  opacity: 1;
}

/* 完全自訂分頁 */
.swiper-pagination-bullet-custom {
  width: 30px;
  height: 4px;
  border-radius: 2px;
}
css

!important 通常不需要,Swiper 的 specificity 不算高,正常 CSS 就能覆寫。


踩坑 6:loop 模式下 onSlideChange 觸發兩次

loop: true 時,Swiper 內部用「複製首尾 slide」實現無縫循環,會讓 onSlideChange 在切換到複製品時觸發一次,真實品又觸發一次。

解法:用 realIndex 取代 activeIndex:

<Swiper
  loop
  onSlideChange={(swiper) => {
    const realIndex = swiper.realIndex;  // ⭐ 不要用 swiper.activeIndex
    setCurrentSlide(realIndex);
  }}
>
tsx

替代選擇

套件大小特色
Swiper~150KB全功能,但較重
Embla Carousel~10KB現代輕量,API 漂亮
Keen Slider~20KB體積小、效能好
react-slick~50KBjQuery slick 移植,老牌穩定
2026 年建議
新專案優先 Embla Carousel(現代、輕、TypeScript 完整)。Swiper 已不是首選,但如果需要它特有的功能(parallax、3D effect)還是 OK。

目錄

    ◆ 相關文章

    • Next.js Add Font Awesome

      2026-05-09
    • Ladle 前端測試工具

      Ladle 前端測試工具

      2026-05-09
    • Human套件教學

      Human套件教學

      2026-05-09
    • HTML React 打字機效果

      2026-05-09
    ← 上一篇日本旅遊規劃APP大對比 (無業配)下一篇 →Termly 第三方cookie教學

    ◆ 關於作者

    Kurau

    個人寫作 / 創作的 SoT,記錄遊戲、影視、學習與生活。

    更多 Kurau 的文章