• トップ
  • ブログ一覧
  • Vue.jsでSEO対策した無限スクロール(Infinite Scroll)を実現する
  • Vue.jsでSEO対策した無限スクロール(Infinite Scroll)を実現する

    メディアチームメディアチーム
    2020.07.14

    IT技術

    「Vue.js」で無限スクロールする方法とは?

    無限スクロール(Infinite Scroll)」は、ページを切り替えることなくページネーションするテクニックです。

    ページの一番下までスクロールすると、表示するコンテンツがなくなるまで、続きを自動的に読み込んでくれます。

    スクロール操作が基本のモバイル端末で、「最もスマートなページネーション」とも言われています。

    今回は、人気の「Vue.js」で無限スクロールを実装する方法と、無限スクロールに必要な SEO対策について解説していきます。

    無限スクロールの実装パターンと SEO 対策

    無限スクロールには、2つの実装パターンがあります。

    1. 同一URLで、ページ内にコンテンツを読み込ませる方法
    2. 読み込むコンテンツ単位にURLを割り当ててページを分割する方法

    それぞれの実装パターンを、SEOの観点で見ると次のような特徴があります。

    1.同一URLで、ページ内にコンテンツを読み込ませる

    この方法のメリットは、実装がシンプルで楽なことです。

    しかし、この方法は、SEO 的には NG な実装パターンです。

    なぜなら、検索エンジンの bot は、スクロールなしで見える情報しか認識できないからです。

    また、この仕様は、ユーザービリティ的にも良くありません。

    ページを訪れるたびに、毎回下までスクロールするのは面倒ですよね。

    2.読み込むコンテンツ単位にURLを割り当ててページを分割

    スクロールで読み込むコンテンツの単位(ページ単位)に、URLを割り当てる方法です。

    これが、SEO的にも良いと言われている実装パターンです。

    実装パターン実際どうやるの?

    下記の画像を見てください。

    全部で40件のコンテンツを、20件ずつページ分けして表示する」と仮定します。

    まず、コンテンツ20件ごとにURLを割り当てます。

    そして、スクロールでページが切り替わるタイミングで「History API」のpushState()replaceState()を使って、URLを書き換えます。

    ページごとに URL を割り当てるメリット

    ページ毎にURLを割り当てることで、検索エンジンのクローラーが情報を全てインデックスできるのです。

    「同一URLでページ内にコンテンツを読み込ませる方法」に比べ、実装が複雑なのが難点ですが…。

    無限スクロールを実装するなら、SEO 対策してあるこちらのパターンを使いましょう。

    Vue.js で無限スクロールを実現する方法

    Vue.js には、標準で無限スクロールを実現する機能がありません。

    そのため、以下のどちらかの方法で、SEO 対策済みの無限スクロールを Vue.js で実装しましょう。

    1. 外部のライブラリを使う
    2. 自前で無限スクロールを実装する

    注意

    ただし、外部のライブラリを使う場合でも、一部の処理は自前で実装しなければなりません。

    SEO 対策済みの無限スクロールライブラリが、公開されていないからです。

    今回の方針

    今回は、無限スクロールを「vue-infinite-loading」という外部のライブラリを使って実現します。

    そして、SEO 対策の為の URL 切り替えを、「History API」を使って自前で実装していきます。

    実装前の準備

    事前準備として、新しいプロジェクトを用意しておきましょう。

    npmvue-cliなど、Vue を開発するための環境が揃っている前提で話を進めます。

    新しいプロジェクトを作る

    まず、vue-cliで、新しいプロジェクトを作成します。

    今回は、「sample-inifnite-scroll」という名前にしました。

    1vue create sample-inifnite-scroll

    vue-infinite-loading をインストール

    プロジェクトが作成されたら、次に「vue-infinite-loading」をインストールします。

    以下のいずれかの方法で、インストールしてください。

    npm の場合

    1npm install vue-infinite-loading -save

    yarn の場合

    1yarn add vue-infinite-loading

    CDN 経由で使用する場合は、以下のタグを html に追加します。

    1<script src="https://unpkg.com/vue-infinite-loading@^2/dist/vue-infinite-loading.js"></script>

    【Vue-infinite-loading】
    https://peachscript.github.io/vue-infinite-loading/

    これで、プロジェクトの準備ができました。

    Vue.js で SEO 対策をした無限スクロールを実装

    それでは、さっそく Vue.js で無限スクロールを実装していきましょう。

    App.vueを開き、次の通りに編集してください。

    Template

    1<template>
    2  <div id="app">    
    3    <!--リストを表示する部分-->
    4    <ul class="list">
    5      <li class="item" v-for="(item, index) in this.items" :key="index">{{item}}</li>
    6    </ul>
    7    <!--下スクロールした時に、次のページのデータを取得する無限スクロールコンポーネント-->
    8    <infinite-loading v-if="hasNext" @infinite="infiniteHandler" spinner="spiral" direction="bottom">
    9      <div slot="no-more">No more</div>       <!--これ以上表示するデータがない時に表示されるメッセージ-->
    10      <div slot="no-results">No results</div> <!--検索結果がない時に表示されるメッセージ-->
    11    </infinite-loading>
    12  </div>
    13</template>

    テンプレートには、「検索したデータを表示する為のリスト部分」と、「無限スクロールを実現するためのinfinite-loadingタグ」を置きます。

    infinite-loadting の役割

    infinite-loadingは、ページの下の方までスクロールすると、@infiniteイベントを発生させます。

    そして、イベントハンドラのinfiniteHandler関数の中で、次のページに表示するデータを検索してくれます。

    JavaScript

    1import InfiniteLoading from 'vue-infinite-loading'
    2
    3export default {
    4  name: 'App',
    5  components: {
    6    InfiniteLoading
    7  },
    8  data() {
    9    return {
    10      items: [],            // リストに表示するデータ
    11      startPage: 0,         // 開始ページ番号
    12      endPage: 0,           // 終了ページ番号
    13      totalPages: 0,        // 総ページ数
    14      pageSize: 20,         // 1ページに表示するデータ件数
    15      initialized: false    // 初回データアクセスが完了した後にtrueを設定するフラグ
    16    }
    17  },
    18  computed: {
    19    hasNext() {
    20      return this.initialized && this.totalPages > this.endPage
    21    }
    22  },
    23  mounted() {
    24
    25    // 現在表示中のページ番号をURLに設定する為に、スクロールイベントを監視
    26    window.addEventListener("scroll", () => this.scroll())
    27
    28    const urlParams = new URLSearchParams(window.location.search);
    29    const page = urlParams.get('page');
    30
    31    if (page) {
    32      // URLパラメータでページ番号が指定された場合、指定ページから表示
    33      this.startPage = parseInt(page, 10)
    34      this.endPage = parseInt(page, 10)
    35    } else {
    36      // ページ番号の指定がない場合は1ページ目から表示
    37      this.startPage = 1
    38      this.endPage = 1
    39    }
    40
    41    // 初回データアクセス
    42    this.getItems(null, this.startPage, false)
    43  },
    44  methods: {
    45
    46    // スクロール時に、次ページに表示するデータを取得する処理
    47    infiniteHandler($state) {
    48      if (this.endPage >= this.totalPages) {
    49        // 表示するデータが無くなったら$state.complete()を呼ぶ
    50        $state.complete()
    51      } else {
    52        // 表示するデータがある場合、時ページのデータを読み込む
    53        this.getItems($state, this.endPage + 1, true)
    54      }
    55    },
    56
    57    // ページに表示するデータを検索する処理
    58    getItems($state, page, next) {
    59      setTimeout(() => {
    60
    61        // 読込データを設定(実際はaxiosなどで非同期でデータを取得する想定)
    62        let data = []
    63        for (let i = 1 ; i <= this.pageSize; i++) {
    64          data.push(`item${page}-${i}`)
    65        }
    66        // 総ページ数を設定(これも実際はaxiosなどで非同期でデータを取得する想定)
    67        this.totalPages = 10
    68
    69        // 現在表示しているデータの末尾に取得したデータを追加
    70        this.items = this.items.concat(data)
    71        this.endPage = page
    72
    73        // $state.loaded()でデータの読込完了を通知する
    74        if ($state) $state.loaded()
    75
    76        this.$nextTick(() => {
    77          this.initialized = true
    78        })
    79      }, 1000)
    80    },
    81
    82    // スクロールイベント発生時の処理
    83    scroll() {
    84      // 現在のスクロールY座標から、画面に表示されているページ番号を計算する
    85      let scroll_pos = window.pageYOffset || document.documentElement.scrollTop
    86      let window_height = window.outerHeight
    87      let page = Math.ceil((scroll_pos + 0.5 * window_height) / 40 / this.pageSize) + (this.startPage - 1)
    88      // replaceStateでurlを書き換え(urlパラメータにページ番号を設定)
    89      window.history.replaceState(null, null, "/?page=" + page)
    90    }
    91  }
    92}

    少し長いコードですが、概ね次のような処理をしています。

    mounted()

    mounted()は、ページロード時に呼ばれます。

    URLパラメータで指定されたページ番号のデータを表示します。

    ページ番号の指定がなければ、1ページ目が表示されます。

    infiniteHandler

    infiniteHandlerは、次のページのデータが必要になった時に呼ばれます。

    次ページのデータを読み込んだ後、リストの末尾にデータを追加します。

    $state.loaded() と $state.completed()

    データを読み込む際、次のページに表示するデータが存在する場合は、$state.loaded()が呼ばれます。

    これ以上表示データがない場合(最終ページ)は、$state.completed()を呼びます。

    scroll 関数

    scroll関数は、ブラウザでスクロールが発生すると呼ばれます。

    現在のスクロール位置(Y座標)から表示中のページ番号を計算します。

    そして、「History API」のreplaceState()URLパラメータに現在のページ番号をセットします。

    CSS

    1.list {
    2  list-style: none;
    3  margin: 0;
    4  padding: 0;
    5}
    6.list li {
    7  height: 40px;
    8  border-top: solid 1px #ccc;
    9  border-bottom: solid 1px #777;
    10  box-sizing: border-box;
    11  margin: 0;
    12  padding: 0;
    13}

    無限スクロールを実行

    ここまで出来たら、実際に動きを確認してみましょう!

    ターミナル上で、次のコマンドを入力します。

    1npm run serve

    1ページ目

    ブラウザでhttp://localhost:8080にアクセスしてみてください。

    1ページ目の情報がリストになった状態で、ページが表示されていると思います。

    では、下にスクロールして、無限スクロールが働いているか確認しましょう。

    2ページ目

    次の画像のように、下スクロールでローディングアイコンが表示された後、2ページ目の情報が表示されればOKです。

    さらにスクロールを続けていきます。

    2ページ目の情報が中盤あたりにくると、「History API」のreplaceState()の処理により、URLパラメータにページ番号がセットされています。

    URL にページ番号を指定する

    また、URLにページ番号を直接指定すると、指定したページ番号のページからリストが表示されます。

    さいごに

    今回は、Vue.js でSEO対策した無限スクロールを実現する方法を紹介しました。

    無限スクロールは、スマホ用サイトなどで、便利にページネーションする為のテクニックです。

    検索エンジンのクローラーがインデックスしやすいように、SEO 対策もしっかりしておきましょう。

    こちらの記事もオススメ!

    featureImg2020.08.07JavaScript 特集知識編JavaScriptを使ってできることをわかりやすく解説!JavaScriptの歴史【紆余曲折を経たプログラミン...

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    メディアチーム
    メディアチーム
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background