5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails7】SwiperをCDN・NPMで導入する方法とtype="module"で詰まったポイント

5
Last updated at Posted at 2026-03-18

はじめに

RailsでSwiperを公式の使用方法を参考に実装してみたので詰まったところも含め、備忘録として記事にしてみました!

Swiperとは?

JavaScriptの拡張機能(ライブラリ)で
スライドショーの様に1つの範囲内でトップや、お知らせ・更新情報の表示などのコンテンツを切り替えられるスライダー機能です。
下記公式のデモンストレーションを見るとイメージしやすいです。

前提条件

Ruby on Rails 7.2.3
Ruby 3.4.8
tailwindCSS
Docker環境

導入方法にはいくつか種類があるそうなのですが、今回はサーバー経由のCDN方式とインストールするNPM方式の2種類を記事化します。
基本的には公式の導入方法に則って進めていきます。

CDNとは?

Content Delivery Networkの略称で、
JavaScriptやCSSなどのファイルをインターネット上で配信しているサーバーのことです。
このCDNを使用することで、自分のプロジェクトにファイルをインストールせずに、そのサーバーから直接読み込むことができる導入方法になります。

CDNでの導入方法

CSSファイルの読み込み

サーバーにあるSwiperを読み込むために、ビューファイルに以下を記述します。

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= content_for(:title) || "Myapp" %></title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= yield :head %>

    <link rel="manifest" href="/manifest.json">
    <link rel="icon" href="/icon.png" type="image/png">
    <link rel="icon" href="/icon.svg" type="image/svg+xml">
    <link rel="apple-touch-icon" href="/icon.png">
+   <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.css"/>
    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>    
  </head>

ここではSwiperのCSSファイルを読み込んでいます。
これを<head></head>の中に記載します。
headはビューページ表示前に読み込まれるので、CSSをここに書くと最初からデザインが適用された状態で表示されます。

<解説>
linkは外部ファイルをHTMLに読み込むためのタグです。
relはrelationshipの略で、「読み込むファイルとの関係」を指定します。ここではstylesheetになるので、ブラウザに「このファイルはCSSですよ」と伝えています。
hrefはhypertext referenceの略で「読み込むファイルのURL(場所)」を指定します。

JavaScriptファイルの読み込み

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
  
   ~~~省略~~~
   
   <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.css"/>
    
   ~~~省略~~~
    
  </head>
  <body>
    <%= yield %>
+   <script src="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.js"></script>
  </body>

これはSwiperのJavaScriptファイルを読み込んでいます。これがないとスライダーが動きません。
必ず</body>の直前に記載します。
JavaScriptはHTMLの要素を操作するので、操作対象のHTMLが全部読み込まれた後に実行する必要があるからです。
また、<head></head>の中に書くと、対象要素がまだ読み込まれていない状態で実行し、HTMLがまだ存在しない状態でJavaScriptが動こうとしてエラーになる可能性があります。

<解説>
scriptはJavaScriptファイルを読み込むためのタグです。
srcはsourceの略で「読み込むファイルのURL(場所)」を指定します。

ビューにスライダーを実装

実際にスライダー機能を入れたいビューファイルに記載します。
今回は簡易的に実施するため、homeに記述しています。

app/views/home/index.html.erb
<div class="swiper">
  <div class="swiper-wrapper">
    <div class="swiper-slide">Slide 1</div>
    <div class="swiper-slide">Slide 2</div>
    <div class="swiper-slide">Slide 3</div>
  </div>
</div>

(オプションは記載してません)
これで最低限のスライダーの中身が完成します。(まだ動きませんし、動かせません)
<解説>
<div class="swiper">
swiperの箱を作っています(外枠を作っている)。
<div class="swiper-wrapper">
実際にswiperする画面で、横に並べる箱を作っています。
<div class="swiper-slide">Slide 1</div>
これが実際にスライダーする画面の中身です。

実際に画面にスライダーが表示されます

※わかりやすく見せるために装飾しています。
app/assets/stylesheets/application.tailwind.css
@import "tailwindcss";

.swiper {
  width: 600px;
  height: 300px;
}

.swiper-slide {
  background: lightblue;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
}

image.png

Swiperを初期化する

Swiperの使用方法の最後に(日本語訳したもの)

Swiperを初期化します
最後に、JavaScriptでSwiperを初期化する必要があります。

と出てきます。いきなりよくわかりません。どういう意味でしょうか。

初期化とは?

初期化とは、「ただの物体」に「役割とルール」を与えて、スイッチを入れることです。

ここでいうと、ただのデータ(HTML)に機能を与えて、使える状態にすること。
現状ビューファイルに書いたコードはただの箱(HTML)です。
このただの箱にnew Swiper('.swiper', {...});とすることで、スライダーという機能付きオブジェクトに変わります。

初期化実装

実際にただの箱(HTML)をスライダー機能付きオブジェクトに変身させましょう。

app/javascript/application.js
const swiper = new Swiper('.swiper');

(オプションは記載してません。)

<解説>
const swiper
swiperという名前で保存しています。後でオプションの指示を出せる様にするためです。
new Swiper
Swiperという設計図から新しく実体を作っている。
('.swiper')
どのHTML要素をスライダーに変えるか指定しています。ここでは「.swiper」というクラス名を指定。

すると以下の様にスライダー機能を使うことができます。

オプションでページネーションやナビゲーションボタンを入れると見やすいです。

app/views/home/index.html.erb
<div class="swiper">
  <div class="swiper-wrapper">
    <div class="swiper-slide">Slide 1</div>
    <div class="swiper-slide">Slide 2</div>
    <div class="swiper-slide">Slide 3</div>
  </div>
  
+ <!-- ページネーション -->
+ <div class="swiper-pagination"></div>
  
+ <!-- ナビゲーションボタン -->
+ <div class="swiper-button-prev"></div>
+ <div class="swiper-button-next"></div>
</div>
app/javascript/application.js
- const swiper = new Swiper('.swiper');

+ const swiper = new Swiper('.swiper', {
+  pagination: {
+    el: '.swiper-pagination',
+  },
+
+  navigation: {
+    nextEl: '.swiper-button-next',
+    prevEl: '.swiper-button-prev',
+  },
+});

image.png

NPMとは?

Node Package Managerの略称で
JavaScriptのライブラリ(拡張機能)を管理するツールです(RailsでいうBundler)。
SwiperはJavaScriptのライブラリ(拡張機能)なのでこのツールを使用してSwiperを導入します。

NPMでの導入方法

SwiperをNPMからインストールする

コマンドを実行してSwiperをインストールする

ターミナル
docker compose exec web npm install swiper

実行したら、package.jsonにswiperがインストールされているか確認する。

package.json

  "dependencies": {
    "@hotwired/stimulus": "^3.2.2",
    "@hotwired/turbo-rails": "^8.0.23",
    "@tailwindcss/cli": "^4.2.1",
+   "swiper": "^12.1.2",
    "tailwindcss": "^4.2.1"
  }
}

dependenciesは「このプロジェクトが動くために必要なライブラリの一覧」みたいな意味。
RubyのGemfileに例えるとgem 'swiper'gemと同じ

初期化実装

初期化の説明は先ほど説明した内容と同じです。
NPMでの導入も同様に、application.jsファイルに記載します。
前回と異なる点としてswiperと

app/javascript/application.js
import Swiper from 'swiper';
import 'swiper/css';


const swiper = new Swiper('.swiper');

<解説>
import Swiper from 'swiper';
Swiperの機能(JS)を読み込みます。
正確にいうと、node_modules の中にある swiper パッケージから Swiper クラスを取り出しています。
Rubyでいうとrequire 'swiper'と同じような意味です。

import 'swiper/css';
Swiperの見た目(CSS)を読み込みます。
CSSの装飾はapplication.cssに記載すると、反映されます。

※tailwindCSSを使っている場合は異なります
app/assets/stylesheets/application.tailwind.css
@import "tailwindcss";
+ @import "swiper/css";

.swiper {
  width: 600px;
  height: 300px;
}

.swiper-slide {
  background: lightblue;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
}

@import "swiper/css";で、SwiperのCSSファイルを読み込んで、このCSSの中に取り込んでいます。
その代わり、JavaScriptファイルで取り込んでいる記述を取り消す。

app/javascript/application.js
import Swiper from 'swiper';
- import 'swiper/css';


const swiper = new Swiper('.swiper');

あとは、同様にビューに記載するとスライダー機能を使うことができます。

(補足)CDNにおけるSwiperのリンクを読み取るのが遅いのにSwiperが正常に機能する理由

const swiper = new Swiper('.swiper');

Swiperは新しいSwiperクラスを作成して、const swiperに変数として保存しています。
このSwiperクラスはCDNではapplication.html.erbファイルで取り込んでいる</body>直前のリンクから読み込まれています。

整理すると、

① Swiperのリンクを読み込み、Swiperクラスが使える状態になる
② application.jsで実行しnew Swiper() が問題なく動く

が①定義→②実行です。

これを基準に考えると

①定義しているファイル swiperのリンクがあるjs
②実行しているファイル application.js

になります。

しかし、ネットワークタブをリロードして確認してみると、以下の順でファイルが読み込まれていました。
image.png

ネットワークタブはブラウザが実際に読み込んだ順番(正確にはリクエスト開始順)に上から並びます。
実際に読み込まれている順番は

①実行しているファイル swiper-bundle.min.js(上から6番目)
②定義しているファイル application.js(上から4番目)

と①定義→②実行するという逆の順番でした。

定義する前にSwiperを実行してしまっているので本来であれば
Swiper is not defined(Swiperって何?)とコンソールが返す形になるはずですが問題なく機能しています。

type: "module"による重要な挙動

結論として、application.html.erbファイルに記載している、

<%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>

このtype: "module"の仕様が原因でした。

<解説>
javascript_include_tag
<script src="...">を生成するRailsのヘルパーメソッドで読み込むファイルのURL(場所)」を指定します。
"application"
実際に読み込むファイルです。
"data-turbo-track": "reload"
Turbo(Hotwire)の機能で、このファイルが変更されたとき自動的にページをリロードします。

type: "module"

この記述により、最終的には以下の形になります

<script type="module" src="/assets/application.js"></script>

つまり、このJSは「モジュール」として扱うという意味。
これによりapplication.js には type="module" が付与されるようになっています。

module属性による実行遅延

MDN Web Docsの「モジュールとクラシックスクリプトとのその他の違い」にmoduleについて以下の様に記載されていました。

モジュールのスクリプトを読み込むときに defer 属性を使う必要はありません。モジュールは自動的に遅延実行されます。

通常ブラウザはHTMLを上から順番に読んでいき、途中にscriptがあればHTMLの読み込みを中断し、scriptを実行します。その際HTMLの読み込みを一度中断してしまうので挙動が遅くなります。
しかしdefer属性はHTMLの途中でscriptがあったとしても実行されず、ダウンロード(読み込み)自体はHTMLと並行して行います。HTMLを読み終えた後一番最後に、ダウンロード(読み込み)が終了したscriptを実行する機能を持っています。

つまり、module属性はその実行を後回しにするdefer属性の機能と類似の機能を持ち合わせています。

よって、ブラウザが実際に読み込んだ順番と異なってもscriptの実行が最後まで遅延され、Swiperが問題なく機能します。

終わりに

今回はSwiperの導入方法だけでなく、なぜ動くのかという仕組みまで調べてみました。
むずかしかった!
「なんとなく動いている」を減らしていくことで、理解が深まると感じたので今後も意識していきたいと思います。
誰かの参考になれば嬉しいです!

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?