LoginSignup
2
1

More than 1 year has passed since last update.

【Vue.js初心者向け】比べて分かる! jQueryで作ったスライダーをVue.jsに落とし込んでVue.jsを理解しよう

Last updated at Posted at 2022-06-07

1.はじめに

大学生フリーランスの むささび です.
主にWeb制作活動を行っています.

普段は JavaScript のライブラリとして jQuery を触る機会が多いですが,モダンなフレームワークにも大変興味があるお年頃です.

そこで,昨今大変人気な JavaScript フレームワークのひとつである Vue.js についてお勉強しました.

本記事ではモダンな JavaScript フレームワーク初心者向けに,Vue.js でスライダーを作成します.
また jQuery との比較もしたかったため,jQuery と Vue.js を用いて同じ機能のスライダー (計2つ) を実装します.

また,今回作成したスライダーは こちら ですが,Vue.js を初めて触って 3日 で完成させました.
この際の勉強方法も参考になるのではと思い,辿ったルートも簡単に紹介したいと思います.

こんな方に読んでほしい

  • Vue.js を触ってみたい
  • jQuery をよく使うけど,モダンなフレームワークを触りたい
  • Vue.js の勉強方法を知りたい

2.完成までの道のり

本記事の目標は 「Vue.js でスライダーを作成する!」 です.
この目標を達成するために,次の流れで学習を行いました.

学習の流れ
1日目: Vues.js の基礎を学ぶ
2日目: jQuery でスライダーを実装する
3日目: Vue.js でスライダーを実装する

1日目

まず1日目では たにぐちまこと さんの YouTube 動画で Vue.js の基礎を学習しました.

上記の動画で使用されている Vue.js のバージョンは 2.x 系ですが,スライダーの実装では 3.x 系を使用したかったため,自分で 3.x 系に書き換えながら学習を進めました.

3.x 系に書き換えを行う際は,主に 公式リファレンス を参考にしました.

2日目

2日目では Vue.js から離れて jQuery を用いてスライダーを実装しました.
作成したスライダーの説明は後ほどします.

3日目

この日が本命です.Vue.js を使用してスライダーを実装します.
2日目に作成したスライダーと全く同じ機能をもつスライダーを,Vue.js で作成します.

3.作成するスライダー

それでは今回作成するスライダーの概要を説明します.
スライダーのコード全体は GitHub に置いておりますので適宜ご参照ください.

今回実装するするスライダーは こちら です.

slider.gif

このスライダーはかなり多機能なものになっており,以下の機能が備わっています.

スライダーの機能一覧

  • ナビゲーションボタン (前後のスライドに移動させる)
  • インジケーターボタン (任意のスライドに移動させる)
  • 自動再生
  • 一時停止

上記の機能が備わったスライダーを,jQuery と Vue.js の 2 方向から実装します.

4.実装

それでは実装詳細です.
jQuery と Vue.js の 2 パターンについて,それぞれ説明します

4-1.jQuery スライダー

jQuery を用いたスライダーの実装を説明します.
実装に使用した jQuery のバージョンは 3.6.0 です.

以下が jQuery スライダーにあたる部分のコードです.
以降でいくつかの箇所を掻い摘んで説明します.

index.html
  <!-- jQuery -->
  <div class="sec-slider jquery">
    <h1>jQuery Slider</h1>
    <div class="slider-wrap">
      <div class="slides">
        <div class="slide"><img src="https://source.unsplash.com/T-VS-7y_fAY/960x300" alt="スライド1"></div>
        <div class="slide"><img src="https://source.unsplash.com/YKtxjGsl1BY/960x300" alt="スライド2"></div>
        <div class="slide"><img src="https://source.unsplash.com/oZu6mNLGJrU/960x300" alt="スライド3"></div>
        <div class="slide"><img src="https://source.unsplash.com/FoBq12oj6SY/960x300" alt="スライド4"></div>
        <div class="slide"><img src="https://source.unsplash.com/kCes633Hh1M/960x300" alt="スライド5"></div>
      </div>
      <div class="slider-nav">
        <a href="#" class="prev"></a>
        <a href="#" class="next"></a>
      </div>
    </div>
    <div class="indicator"></div>
  </div><!-- /jQuery -->
script.js
$(function(){
  
  /*
   * jQuery Slider
   */
  $('.sec-slider.jquery').each(function (){
    
    var $section    = $(this),
        $slides       = $section.find('.slider-wrap .slides'),
        $slide        = $slides.find('.slide'),
        $nav          = $section.find('.slider-wrap .slider-nav'),
        $indicator    = $section.find('.indicator'),

        slideCount    = $slide.length,
        indicatorHTML = '',
        currentIndex  = 0,
        interval      = 7500,
        timer;

    $slide.each(function (i) {
      indicatorHTML += '<a href="#"></a>';
    });
    $indicator.html(indicatorHTML);


    // 関数の定義
    // - - - - - - - - - - - - - - - - - - - - 

      // 次のスライドを表示する関数
      function showSlide(index) {
        var nextIndex = index;

        $slide.eq(currentIndex).fadeOut();  // 現在のスライドをフェードアウト
        $slide.eq(nextIndex).fadeIn();      // 次のスライドをフェードイン
        currentIndex = nextIndex;           // インデックを更新
        update();                           // ナビゲーションとインジケーターの更新 
      }

      // ナビゲーションとインジケーターを更新する関数
      function update() {
        var $prev = $nav.find('.prev');
            $next = $nav.find('.next');

        // 最初のスライドであれば,$prev ナビゲーションを無効にする
        if(currentIndex == 0){
          $prev.addClass('disabled');
        } else {
          $prev.removeClass('disabled');
        }
        // 最初のスライドであれば,$next ナビゲーションを無効にする
        if(currentIndex == slideCount-1){
          $next.addClass('disabled');
        } else {
          $next.removeClass('disabled');
        }
        // 現在のスライドのインジケーターを有効化
        $indicator.find('a').removeClass('active')
                  .eq(currentIndex).addClass('active');
      }

      // タイマーを開始する関数
      function startTimer() {
        timer = setInterval(function () {
          // 次に表示するスライドのインデックス
          var nextIndex = (currentIndex+1) % slideCount;
          showSlide(nextIndex);
        }, interval);
      }

      // タイマーを停止する関数
      function stopTimer() {
        clearInterval(timer);
      }


    // イベントの登録
    // - - - - - - - - - - - - - - - - - - - - 

      // ナビゲーションがクリックされたら該当するスライドを表示
      $nav.on('click', 'a', function(event) {
        event.preventDefault();
        if($(this).hasClass('prev')) {
          showSlide(currentIndex-1);
        } else {
          showSlide(currentIndex+1);
        }
      });

      // インジケーターがクリックされたら該当するスライドを表示
      $indicator.on('click', 'a', function(event) {
        event.preventDefault();
        if( !$(this).hasClass('active') ){
          showSlide($(this).index());
        }
      });

      // マウスが乗ったらタイマーを停止
      $slides.on({
        mouseenter: stopTimer,
        mouseleave: startTimer
      });


    // スライドショーの開始
    // - - - - - - - - - - - - - - - - - - - - 
    showSlide(currentIndex);
    startTimer();
  });
});

※補足 スライドの画像に用いる API

jQuery と Vue.js との両者とも,スライド画像は Unsplash Source の API を用いています.

Unsplash はオシャレな無料画像を探すのに非常に便利なサイトで,その Unsplash が提供している API が Unsplash Source になります.

例えば,ランダムな画像を取得したい場合は

https://source.unsplash.com/random

と入力すれば OK です.
サイズの指定も可能で大変便利ですね.

次のスライドを表示する関数

      function showSlide(index) {
        var nextIndex = index;

        $slide.eq(currentIndex).fadeOut();  // 現在のスライドをフェードアウト
        $slide.eq(nextIndex).fadeIn();      // 次のスライドをフェードイン
        currentIndex = nextIndex;           // インデックを更新
        update();                           // ナビゲーションとインジケーターの更新 
      }

上記の関数でスライドのフェードイン/フェードアウトを操作しています.
フェードインには jQueryの fadeIn() メソッド,フェードアウトには fadeOut() メソッド を用いています.

また update() 関数は次で定義しているナビゲーションとインジケーターを更新する関数です.

ナビゲーションとインジケーターを更新する関数

      function update() {
        var $prev = $nav.find('.prev');
            $next = $nav.find('.next');

        // 最初のスライドであれば,$prev ナビゲーションを無効にする
        if(currentIndex == 0){
          $prev.addClass('disabled');
        } else {
          $prev.removeClass('disabled');
        }
        // 最初のスライドであれば,$next ナビゲーションを無効にする
        if(currentIndex == slideCount-1){
          $next.addClass('disabled');
        } else {
          $next.removeClass('disabled');
        }
        // 現在のスライドのインジケーターを有効化
        $indicator.find('a').removeClass('active')
                  .eq(currentIndex).addClass('active');
      }

上記の関数ではナビゲーションとインジケーターの動きを管理しています.

このスライダーでは,最初のスライドが表示されているときは 「前の」 スライドへ移動する矢印が非表示になり,また最後のスライドが表示されているときは 「次の」 スライドへ移動する矢印が非表示になります.

BFE7E00C-8C8B-456A-8B4D-5054258F449C.jpg

update 関数ではこの動きを実装しています.
さらに,現在のスライドに該当するインジケーターボタンを 有効化 (黒色) にしているのもこの関数です.

タイマーを開始/停止する関数

      // タイマーを開始する関数
      function startTimer() {
        timer = setInterval(function () {
          // 次に表示するスライドのインデックス
          var nextIndex = (currentIndex+1) % slideCount;
          showSlide(nextIndex);
        }, interval);
      }

      // タイマーを停止する関数
      function stopTimer() {
        clearInterval(timer);
      }

スライダーの機能に 自動再生/一時停止 がありますが,この機能を司っている関数が startTimer()stopTimer() になります.

startTimer() では JavaScript のメソッドである setInterval() を用いて,showSlide() 関数を定期的に呼び出しています.

また stopTimer() では clearInterval() を使用し,setInterval() の呼び出しによって確立されたタイマーの動作を取り消しています.

イベントの登録

      // ナビゲーションがクリックされたら該当するスライドを表示
      $nav.on('click', 'a', function(event) {
        event.preventDefault();
        if($(this).hasClass('prev')) {
          showSlide(currentIndex-1);
        } else {
          showSlide(currentIndex+1);
        }
      });

      // インジケーターがクリックされたら該当するスライドを表示
      $indicator.on('click', 'a', function(event) {
        event.preventDefault();
        if( !$(this).hasClass('active') ){
          showSlide($(this).index());
        }
      });

      // マウスが乗ったらタイマーを停止
      $slides.on({
        mouseenter: stopTimer,
        mouseleave: startTimer
      });

上記では,これまで定義した関数を呼び出すタイミングを決めています.

ひとつ注意点として,preventDefault() の使い方に着目してください.

event.preventDefault() は通常行われるはずの動作をキャンセルするメソッドです.
ここでは a タグをクリックした際のリンク遷移をキャンセルしているわけですね.

また,スライダーの 自動再生/一時停止 機能についてですが,マウスがスライド画像上に乗っているときにタイマーを止めることで実現しています.

4-2.Vue.js スライダー

さて,ここからが本番です.Vue.js を用いたスライダーの実装を説明します.
実装に使用した Vue.js のバージョンは 3.2.36 です.
Vue.js 公式が提供している CDN を使用しています.

以下が Vue.js スライダーにあたる部分のコードです.
jQueryと同様に以降でいくつかの箇所を説明しますが,プログラムの基本的な考えはほとんど同じです.

index.html
  <!-- Vue.js -->
  <div class="sec-slider vue" id="app">
    <h1>Vue.js Slider</h1>

    <div class="slider-wrap">
      <div class="slides" @mouseenter="stopTimer()" @mouseleave="startTimer()">
        <transition-group name="slide-trans">
          <div class="slide" v-show="currentIndex == id" :key="id" v-for="(img,id) in imgs">
            <img v-bind:src="imgs[id].src" v-bind:alt="'スライド' + (id+1)">
          </div>
        </transition-group>
        </div>
      <div class="slider-nav">
        <a href="#" class="prev" v-show="currentIndex > 0" @click="prev($event)"></a>
        <a href="#" class="next" v-show="currentIndex < slideCount-1" @click="next($event)"></a>
      </div>
    </div>
    
    <div class="indicator">
      <template v-for="i in slideCount" :key="i">
        <a v-if="currentIndex == i-1" class="active" href="#" @click="jump(i-1,$event)"></a>
        <a v-else href="#" @click="jump(i-1,$event)"></a>
      </template>
    </div>
  </div><!-- /Vue.js -->
script.js
window.onload = function(){

  /*
   * Vue.js Slider
   */

  var imgs = [
        { src: 'https://source.unsplash.com/T-VS-7y_fAY/960x300' },
        { src: 'https://source.unsplash.com/YKtxjGsl1BY/960x300' },
        { src: 'https://source.unsplash.com/oZu6mNLGJrU/960x300' },
        { src: 'https://source.unsplash.com/FoBq12oj6SY/960x300' },
        { src: 'https://source.unsplash.com/kCes633Hh1M/960x300' }
      ],
      slideCount    = imgs.length,
      indicatorHTML = '<a href="#"></a>',
      interval      = 7500,
      timer;

  const app = Vue.createApp({
    data() {
      return {
        currentIndex: 0,
        imgs: imgs,
        slideCount: slideCount,
        indicatorHTML: indicatorHTML,
        timer: timer
      }
    },

    // インスタンスが作成された後に同期的に呼び出す
    created: function() {
      this.startTimer();
    },

    // メソッドの定義
    methods: {
      prev(e) {  // 次のスライドを表示
        e.preventDefault();
        this.currentIndex--;
      },
      next(e) {  // 前のスライドを表示
        e.preventDefault();
        this.currentIndex++;
      },
      jump(i,e) {  // インジケーターがクリックされたら該当するスライドを表示
        e.preventDefault();
        this.currentIndex = i;
      },
      startTimer() {  // タイマーを開始する関数
        this.timer = setInterval( () => {
          this.currentIndex = (this.currentIndex+1) % slideCount;
        }, interval);
      },
      stopTimer() {  // タイマーを停止する関数
        clearInterval(this.timer);
      }
    }
  }).mount('#app')
}

変数の定義

  var imgs = [
        { src: 'https://source.unsplash.com/T-VS-7y_fAY/960x300' },
        { src: 'https://source.unsplash.com/YKtxjGsl1BY/960x300' },
        { src: 'https://source.unsplash.com/oZu6mNLGJrU/960x300' },
        { src: 'https://source.unsplash.com/FoBq12oj6SY/960x300' },
        { src: 'https://source.unsplash.com/kCes633Hh1M/960x300' }
      ],
      slideCount    = imgs.length,
      indicatorHTML = '<a href="#"></a>',
      interval      = 7500,
      timer;

jQuery と異なるポイントは,ズバリ img 配列ですね.
オブジェクトを用いて画像の URL をまとめて保持しています.

また Array.length プロパティを用いて,スライドの数を slideCount 変数に格納しています.

メソッドの定義

    methods: {
      prev(e) {  // 次のスライドを表示
        e.preventDefault();
        this.currentIndex--;
      },
      next(e) {  // 前のスライドを表示
        e.preventDefault();
        this.currentIndex++;
      },
      jump(i,e) {  // インジケーターがクリックされたら該当するスライドを表示
        e.preventDefault();
        this.currentIndex = i;
      },
      startTimer() {  // タイマーを開始する関数
        this.timer = setInterval( () => {
          this.currentIndex = (this.currentIndex+1) % slideCount;
        }, interval);
      },
      stopTimer() {  // タイマーを停止する関数
        clearInterval(this.timer);
      }
    }

ますは各メソッドの定義を見てみます.
jQuery で定義した関数と本質的な考えは変わりませんが,かなりシンプルに記述できているように思えます.

スライド表示の動きについては,currentIndex 変数がカギです.
この変数を インクリメント/デクリメント することで前後のスライドへ移動し,さらにクリックされたスライドの番号を渡すことで該当するスライドへ飛ぶことが可能です.

また startTimer(), stopTimer() も定義しています.
jQuery スライダーと同様に setInterval()clearInterval() を用いて,うまくタイマー機能を実装しています.

created による呼び出し

    // インスタンスが作成された後に同期的に呼び出す
    created: function() {
      this.startTimer();
    },

Vue.js には ライフサイクル という概念があり,Vue.jsによって作成されたページはこのサイクルに従って処理が実行されていきます.

created は,このライフサイクルのひとつのタイミングであり,インスタンスが作成されたあとに関数の呼び出しが行われます.
つまり,インスタンスが作成されたあとに startTimer() メソッドが呼び出され,スライドの自動再生が行われるわけですね.

スライド画像の表示

      <div class="slides" @mouseenter="stopTimer()" @mouseleave="startTimer()">
        <transition-group name="slide-trans">
          <div class="slide" v-show="currentIndex == id" :key="id" v-for="(img,id) in imgs">
            <img v-bind:src="imgs[id].src" v-bind:alt="'スライド' + (id+1)">
          </div>
        </transition-group>
      </div>

ここからは HTML コードについて見ていきます.上記はスライダー本体を表示している部分になります.

大きな動きとして v-for ディレクティブ を用いて for ループを回し,img 配列内の画像を表示させています.

また v-for でループを回す際に id(インデックス) を付与しています.
この値と currentIndex 変数とを v-show ディレクティブ を用いて比較し,一致するスライド画像を表示させているという仕組みです.

さらに を使用することで,アニメーションの付与を実現しています.
<transition-group> を使用するには,子要素を識別する key が必要ですが,その key として id を指定しています.

また <transition-group> は,トランジション/アニメーション のためのクラスを自動的に生成するので,そのクラスを使用すれば簡単にアニメーションをつけることができます.
以下のコードが,トランジション効果を適用する CSS になります.

style.css
.sec-slider.vue .slides .slide {
  display: block;
}
.slide-trans-enter-active, .slide-trans-leave-active {
  transition: all .4s ease-out;
}
.slide-trans-enter-from, .slide-trans-leave-to {
  opacity: 0;
}

クラス命名の規則は こちら を参考にしてください.

ナビゲーションの表示

      <div class="slider-nav">
        <a href="#" class="prev" v-show="currentIndex > 0" @click="prev($event)"></a>
        <a href="#" class="next" v-show="currentIndex < slideCount-1" @click="next($event)"></a>
      </div>

上記がナビゲーション (画像を前後へ移動させる) ボタンの表示に関わる部分です.

「前の」 画像に移動するボタンがクリックされたときは prev() を呼び出し,
「 次の」 画像に移動するボタンがクリックされたときは next() を呼び出します.

jQuery スライダーで説明した ナビゲーションとインジケーターを更新する関数 と同様に,最初と最後のスライドでは矢印の表示を制御しています.
この機能を Vue.js ではv-show ディレクティブ を用いることで実現しています.

インジケーターの表示

    <div class="indicator">
      <template v-for="i in slideCount" :key="i">
        <a v-if="currentIndex == i-1" class="active" href="#" @click="jump(i-1,$event)"></a>
        <a v-else href="#" @click="jump(i-1,$event)"></a>
      </template>
    </div>

最後にインジケーターの表示部分の説明です.
Vue.js において <template>様々な役割 があるようですが,ここでは v-for によって複数の子要素を制御するために使用していると思います (自信ないです...ごめんなさい)

また v-if を用いて currentIndex と インジケーターボタンの番号とを比較し,一致したボタンを 有効化(黒色) しています.
それ以外のボタンをクリックすると jump() が呼び出され,該当のスライドへ遷移することが可能です.

5.jQuery vs Vue.js

スライダーの実装,大変お疲れさまでした.
今回の実装をもとに,jQueyr と Vue.js のそれぞれに感じたメリットとデメリットをまとめてみます.

メリット デメリット
jQuery 直感的な操作ができる DOMを直接操作するので状態管理が大変
Vue.js コード量が少なく視認性が高い
仮想DOMを扱うので表示速度が早い/管理が楽
jQueryのメソッドが使えない

Vue.js を支えている技術に 仮想DOM といった技術があります.
この技術により「表示速度の高速化」や「DOM管理の易化」が実現できている (らしい) のです.
この恩恵を少しでも感じることができたと思っております.

Vue.js のデメリットを強いて申し上げると,小さな規模の サイト/サービス では必要ないという点ですかね...

私は普段「Web制作」の分野で生息しており,ちょっとした Web サイトや EC サイトでは,jQueryで事足りてしまうというのが事実です.
jQuery と聞くとレガシーに感じられる方も多いとは思いますが,このライブラリから提供されているメソッドや機能は大変使いやすいです.

Vue.js をはじめとしたモダンなフレームワークの学習/導入コストを考慮すると,「Web制作」の分野では今後も jQuery が広く使われていくのかな...と思います.

6.まとめ

本記事は Web 制作の分野で頑張っている大学生が Vue.js をお勉強してみた!という内容でした.
Vue.js を初めて触って 3 日でアウトプットを出すことができたので,その点は自分を褒めてあげたいなと思います.

また,普段よく使用している jQuery と現在人気なフレームワークのひとつである Vue.js の比較もすることができ,大変勉強になりました.

どなたかの参考になれば幸いです.

むささび

参考

▼ 今回実装した機能のソースコード
https://github.com/musasabibyun/jsfw-sliders

▼ たにぐちまことさん「Vue.js 入門」
https://www.youtube.com/playlist?list=PLh6V6_7fbbo-SZYHHBVFstU2tp0dDZMAW

▼ Vue.js 公式リファレンス (3.x系)
https://v3.ja.vuejs.org/

▼ Unsplash Develpoers
https://unsplash.com/developers

▼ jQuery -fadeIn()
https://api.jquery.com/fadein/

▼ jQuery -fadeOut()
https://api.jquery.com/fadeOut/

▼ setInterval()
https://developer.mozilla.org/ja/docs/Web/API/setInterval

▼ clearInterval()
https://developer.mozilla.org/ja/docs/Web/API/clearInterval

▼ Event.preventDefault
https://developer.mozilla.org/ja/docs/Web/API/Event/preventDefault

▼ Array.length
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/length

▼ Vue.js -ディレクティブ
https://v3.ja.vuejs.org/api/directives.html

▼ Vue.js -条件付きレンダリング
https://v3.ja.vuejs.org/guide/conditional.html#template-%E3%81%A6%E3%82%99%E3%81%AE-v-if-%E3%81%AB%E3%82%88%E3%82%8B%E6%9D%A1%E4%BB%B6%E3%82%AF%E3%82%99%E3%83%AB%E3%83%BC%E3%83%95%E3%82%9A

▼ Vue.js -トランジションクラス
https://v3.ja.vuejs.org/guide/transitions-enterleave.html#%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B7%E3%82%99%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%AF%E3%83%A9%E3%82%B9

▼ Qiita「Vue.js・Reactを触る際に知っておきたい仮想DOMの話」
https://qiita.com/terry_6518/items/4ecb90fa474895b81b8a

2
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
2
1