logo

Vue 使用 Swiper Element (WebComponent)

Swiper 是一個輪播圖套件,提供 JavaScript、React、Vue、Svelte 等方式使用。那為什麼不直接使用 Vue Component 呢?雖然目前還是能使用 Vue Componet,但官方有提到未來將會移除,並推薦使用 Swiper Element (WebComponent)

安裝套件

npm install swiper

註冊

import { register } from 'swiper/element/bundle'

register()

使用

註冊 register() 後,就能使用 <swiper-container><swiper-slide> 這兩個 web component

<swiper-container>
  <swiper-slide>Slide 1</swiper-slide>
  <swiper-slide>Slide 2</swiper-slide>
  <swiper-slide>Slide 3</swiper-slide>
  ...
</swiper-container>

Vue Web Component 設定

預設 Vue 會將非原生 HTML tag 解析為 Vue Component,但實際上我們沒有以上兩個 Component,因此我們為了讓 Vue 知道哪些為自訂元素,需要額外做設定 compilerOptions.isCustomElement

// vite.config.js
import vue from '@vitejs/plugin-vue'

export default {
  plugins: [
    vue({
      template: {
        compilerOptions: {
          isCustomElement: tag => tag.includes('swiper')
        }
      }
    })
  ]
}

參數寫在 <swiper-container> 屬性上

寫在屬性上需要以 [key]-[subkey]="value" 的寫法,或是 Vue 可以用 v-bind,例如以下設定 pagination 的 hideOnClick。

<swiper-container
  slides-per-view="3"
  loop="true"
  :pagination="{
      hideOnClick: true
  }"
>
  <swiper-slide>Slide 1</swiper-slide>
  ...
</swiper-container>

參數太複雜也能使用 HTMLElement 作為傳遞

需要在 <swiper-container> 增加 init="false"

<script setup lang="ts">
import { register } from 'swiper/element'
register()

const params = {
  slidesPerView: 1,
  breakpoints: {
    640: {
      slidesPerView: 2,
    },
    1024: {
      slidesPerView: 3,
    }
  }
}

const swiperEl = ref()
onMounted(() => {
  swiperEl.value = document.querySelector('swiper-container')
  if (swiperEl) {
    Object.assign(swiperEl.value, params)
    swiperEl.value?.initialize()
  }
})
</script>

<template>
  <swiper-container init="false"> ... </swiper-container>
</template>

取得 Swiper instance 操作 Methods

<script setup lang="ts">
...

const clickHandler = () => {
  swiperEl.value?.swiper?.slideNext()
}
</script>

<template>
  <swiper-container init="false"> ... </swiper-container>

  <button @click="clickHandler">
    Slide Next
  </button>
</template>

加入模組 Modules

需要使用 modules 參數來加入模組

<script setup lang="ts">
import { register } from 'swiper/element'
import { Navigation, Pagination } from 'swiper/modules';

register()

const params = {
  modules: [Navigation, Pagination],
  injectStylesUrls: [
    'swiper/element/css/navigation',
    'swiper/element/css/pagination'
  ]
}

const swiperEl = ref()
onMounted(() => {
  swiperEl.value = document.querySelector('swiper-container')
  if (swiperEl) {
    Object.assign(swiperEl.value, params)
    swiperEl.value?.initialize()
  }
})
</script>

<template>
  <swiper-container init="false"> ... </swiper-container>
</template>

WARNING

但這時候發現依照官方的範例,injectStylesUrls 無法正常引入 styles
依照 issues 的解釋
Vite Vue 直接在 injectStylesUrls 使用 swiper/element/css/navigation,實際上會請求 http://localhost:5173/swiper/element/css/navigation 導致找不到。

正確使用方法

import { Navigation } from 'swiper/modules'
import NavigationStyle from 'swiper/element/css/navigation?url'

const params = {
  modules: [Navigation],
  injectStylesUrls: [NavigationStyle],
}

至於為什麼要加 ?url,可以參考 Vite - Static Asset Handling