Nuxt.jsで使用するカルーセルライブラリを選定する
最近使用したvue-ssr-carouselがかなり良かったので、選定基準等と合わせて紹介します。
カルーセルライブラリの選定基準
一般的な選定基準も含みます。
開発環境で使用できる
フロントエンド要件として必須です。
デザインをカスタマイズできる/カスタマイズの自由度が高い
デザイナーの手掛けたカルーセルデザインがコンポーネントライブラリのデザインと同一であることはまずないため、デザイン要件としてほぼ確実に必須です。
学習コストが低い
工数は有限なので、学習コストは低い方が好ましいです。
先述した内容と合わせると、カスタマイズの自由度は高い方が好ましいものの、実装難易度が高かったり仕様が煩雑だったりすると選定はしにくくなってしまいます。
バンドルサイズが小さい
バンドルサイズが大きいと、ダウンロード時間が長くなりFCP(ファーストビューの最初のコンテンツが表示されるまでの時間)等の指標に悪影響を及ぼしてしまいます。フロントエンドとしては重要視したいところです。
バンドルサイズについては、Bundlephobia等で確認することができます。インストール後はwebpack-bundle-analyzer等でも確認できます。
メンテナンスが続いている/メンテナンス頻度が多い、コミッターが多い
メンテナンスが行われていなかったり、行われていても頻度が少ないと、フレームワークのバージョンアップや依存ライブラリに脆弱性が見つかった時等に対応が遅れるか、最悪対応自体が行われない可能性があります。
ダウンロード数/スター数が多い
ライブラリの使用者数と相関がある値なので、多い方が詰まった際に人に聞いたり、インターネットで調べたりすることで解決できる可能性が高くなります。
ライブラリに対する信頼度とも相関はあると思いますが、あくまでそういう傾向にあるというだけなのでこの値だけを見てライブラリを選定をすることはないと思います。少なすぎても怖いですが...。
SSRに対応している
開発環境としてNuxt.jsやNext.jsを選択している場合、ほぼ必須要件であると考えています。
特にカルーセルがファーストビューに入っている場合、SSRに対応していないとLCP(ファーストビューで最大領域を占めるコンテンツが表示されるまでの時間)対策が困難になります。
また、SSRしていないとクローラーやボットがページを正しく評価してくれない可能性があります。
他のカルーセルライブラリ
vue-awesome-swiper (depricated)
有名なライブラリですが現在はdeprecatedとなっており、後述するSwiperに誘導されます。
Swiper
こちらも有名なライブラリであり、他のカルーセルライブラリと比較するとダウンロード数・スター数は圧倒的です。メンテナンスも頻繁に行われており、SSRにも対応しています。
一方で、ライブラリのcssを上書く形でスタイリングする必要があり、それが少し大変です。また、他のカルーセルライブラリと比較するとバンドルサイズが大きい点も気になります。v9.2.0ではMINIFIED+GZIPPEDで37.5kBです(比較対象として、たとえばvue-carouselは同条件で15.6kB)。
vue-carousel
このライブラリも有名ですが、SSRに対応していません。実はvue-carouselにSSRを追加する機能リクエストissueは存在するのですが、statusはopenの状態です。
当該issue内では、SSRで動作しない状況に対して「ssr: false
の使用」や「mounted()
タイミングでの読み込み」が提案されていますが、それらに対する反論が多くの反響を得ていることからも、SSR対応が望まれていたことがわかります。
最後にメンテナンスされてから約4年が経過していることからも、現在このライブラリを積極的に選択する理由はないと思います。
hooper
バンドルサイズがMINIFIED+GZIPPEDで4.9kBと、vue-carouselよりさらに軽いです。SSRにも対応しています。
一方で、SSR対応こそしているものの、
- カルーセルの内容が動的に変化する
- レスポンシブ対応
等のケース、つまりカルーセル読込後にカルーセルの内容が変化するようなケースにおいて、意図しないレイアウト崩れが発生する場合があります。
おそらくですが、JSを使用してカルーセルをレイアウトしているのでしょう。初期表示こそhydration後に正しいレイアウトになるものの、ここでの処理はSSRのタイミングでカルーセルに与えられた情報(カルーセルの数やビューポート等)に合わせたものであるため、その後にそれらの値が変化するとレイアウトが崩れてしまうのだと推測します。書いていて気付きましたが、JSを使用してレイアウトしているなら、SSRに対応していてもLCPには少なからず影響が出ていそうですね...。
一応、一部のレイアウト崩れはカルーセルコンポーネントを適切なタイミングで読み込む/読み込み直す等の方法で回避できるのですが、学習コストとしてはやや高めなのと、最後にメンテナンスされてから約4年が経過している以上、やはり選定はしにくくなっています。
flickity
使用経験なし。
vue-ssr-carouselの「Why another carousel component」に載っています。
- Not a Vue component, so extra work building a Vue wrapper for it.
- No SSR support, delaying LCP scoring.
- When JS hydrates, the slides get nested in a new parent, which affects LCP calculations.
Vueのコンポーネントではない(ので余分な作業が必要となる)点や、SSRに対応していないためLCPスコアに悪影響を及ぼす点が課題として挙げられています。
vue-slick-carousel
使用経験なし。
vue-ssr-carouselの「Why another carousel component」に載っています。
- Slick applies responsive rules only after JS inits. This also results in getting a Mismatching childNodes vs. VNodes error when the page hydrates at a viewport width that changes the slidesToShow.
- It's extra work to make the carousel look the same before and after Slick inits, since you have to style them two different ways.
- Difficulty determining if there's overflow after Slick inits because when Slick is initialized and infinite: true, Slick adds a full set of .slick-cloned slides before the "real" slides, and another full set after them
- Doesn't handle being empty well.
- When using custom arrows or dots, it would show a warning that the Nodes does not match.
- Doesn't do a good job of preventing images and links within slides from preventing dragging.
読む限り、hooperと類似した課題を抱えていそうです。
vue-ssr-carouselについて
SSR/SSG環境向けに設計されたカルーセルライブラリです。
カルーセルのレイアウトにJSを使用していないことが特徴で、これにより従来のカルーセルライブラリが抱えてたLCPやCLSに対する影響の問題を解決しています。
導入
npm install
npm install vue-ssr-carousel
nuxt.config.jsに下記を追加
export default {
buildModules: [ 'vue-ssr-carousel/nuxt' ]
}
サンプル
簡単に書いてみます。
<template>
<div class="container">
<ssr-carousel v-model="page" loop>
<div v-for="(slide, index) in slides" :key="index" :index="index + 1">
<nuxt-img
class="slide-image"
:name="slide.name"
:src="slide.src"
></nuxt-img>
</div>
</ssr-carousel>
<div class="pagination-container">
<div
v-for="(slide, index) in slides"
:key="index"
class="pagination"
:class="{ active: page === index }"
@click="pagination(index)"
></div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
slides: [
{
name: "slide1",
src: "https://placehold.co/600x300/FFAAAA/FFF?text=Slide1",
},
{
name: "slide2",
src: "https://placehold.co/600x300/AAFFAA/FFF?text=Slide2",
},
{
name: "slide3",
src: "https://placehold.co/600x300/AAAAFF/FFF?text=Slide3",
},
],
page: 0,
};
},
methods: {
pagination(index) {
this.page = index;
},
},
};
</script>
<style scoped>
.container {
width: 100%;
max-width: 980px;
margin: 0 auto;
}
.slide-image {
width: 100%;
}
.pagination-container {
display: flex;
justify-content: center;
width: 100%;
}
.pagination {
content: "";
width: 16px;
height: 16px;
margin: 20px 10px 0;
border-radius: 16px;
background-color: #ccc;
}
.pagination:hover {
cursor: pointer;
}
.pagination.active {
content: "";
width: 16px;
height: 16px;
border-radius: 16px;
background-color: #555;
}
</style>
公式のサンプルはこちらをご確認ください。また、使用できるパラメータはGithubに記載されています。
長所
学習コストが低く、デザインをカスタマイズしやすい
「ライブラリのための書き方」というよりは「VueやNuxtで書いたコードにカルーセルの機能を付与するような書き方」ができるので、学習コストが低く、デザインのカスタマイズ性にも優れています。
バンドルサイズが小さい
MINIFIED + GZIPPEDで7.5kBです。
SSRに対応しており、JSによるレイアウト周りの課題もクリアしている
先述の通りです。カルーセルのレイアウトにJSを使用していないことから、LCPやCLSに対する影響の問題を解決しています。
課題
現時点でNuxt 3に対応していない
Vue2のEOLが2023年12月に迫っていることから、現在Nuxt3を使用している場合はもちろん、直近でNuxt3への移行を考えている場合も選択肢から外れることになります。
ただ、Nuxt2も最近遂にv2.16がリリースされ(現在はv2.16.3)、これによりEOLが2024年4月頃まで延長されたので、導入してNuxt3への対応を期待するというのも手だと思います。
おまけ:カルーセルとスライダーの違い
機能的には同一ですが、
- スライダー:一度に1つのスライドを表示する
- カルーセル:一度に複数のスライドを表示する
という使い分けだそうです。
Discussion