 
  こんにちはjunです。前回の記事は詳細ページの実装と静的書き出しを行いました。今回の記事では
について解説していきます。それでは早速いきましょう。
詳細ページのルーティングを作るために以前は以下のようなディレクトリの設定をしました。
├── pages
│   ├── articles
│       ├── _slug.vue
この場合、/articles/{sulg}というURLが有効になります。一覧ページは/articles/というルートでcontent/articles/配下の原稿が一覧となって見れるページです。しかし上記の設定では表示されないので以下のようにします。
├── pages
│   ├── articles
│       ├── index.vue #追加
│       ├── _slug.vue
index.vueというものを足しました。このファイルは/articles/というルートに対応しています。ディレクトリの設定は以上となります。
それではindex.vueにソースを書いていきましょう。詳細ページでは特定のパスに対応するコンテンツを取得していましたが、一覧ページではarticlesのコンテンツを15件ほど取得するようにしましょう。とりあえずソースを載せます。
<template>
    <div class="">
        <h1>記事一覧</h1>
        <ul>
        <li v-for="(c,index) in content" :key="index">
            <nuxt-link :to="c.path">{{c.title}}</nuxt-link>
        </li>
        </ul>
    </div>
</template>
<script>
export default {
  async asyncData({ store,$content, params }) {
    const content = await $content('articles')
    .only(['title','path'])
    .sortBy('createdAt', 'desc')
    .skip(0).limit(15)
    .fetch();
    return {
      content,
    }
  }
}
</script>
まずは$content('articles')でarticles配下のコンテンツを読む指定をします。そして.only(['title','path'])を使用することでtitleとpathのみのデータを取得することができます。このonly()を指定しない場合、bodyプロパティという原稿内容も取得してしまいます。原稿がボリューミーなほど取得コストが大きくなり、静的書き出しなどにも影響されます。そのため一覧などではonly()を使用して必要最低限のプロパティを使用した方がいいです。
sortBy()にて特定プロパティでソートし、後のページングで使いますが.skip(0).limit(15)にて15件の記事を取得します。asyncData()内で記事を取得して、それをリストで出力します。
$content()に対して加えることが可能なメソッドはこちらで確認できます。
Nuxt.jsで内部リンクを作成する時は<nuxt-link>を使用します。toにcontent.pathを指定することで詳細ページに移動できるようになります。
一覧ページはこれぐらいで実装できます。今は最初から15件しか取得しないので、大量にある時はページングができるようにしましょう。
私のサイトでは/articles/page/2の様なルートで対応しています。この様なルートを設定する場合は以下のようにpages/を設定します。
├── pages
│   ├── articles
│       ├── index.vue 
│       ├── _slug.vue
│       ├── pages          #追加
│            ├──_id.vue    #追加
_id.vueを作成することで/articles/page/{n}という動的ルートが作成されます。そこでは以下のように設定します。
<template>
    <div class="">
        <h1>記事一覧</h1>
        <ul>
            <li v-for="(c,index) in content" :key="index">
                <nuxt-link :to="c.path">{{c.title}}</nuxt-link>
            </li>
        </ul>
        <ul class="p-pagenation-container">
            <li class="c-pagenation-unit" v-for="(pg) in num" :key="pg.num">
                <nuxt-link v-if="pg.pg" :to="'/articles/page/'+pg.num" :class="(current == pg.num)?'is-current':''">
                    {{pg.num}}
                </nuxt-link>
                <span v-else>
                    {{pg.num}}
                </span>
            </li>
        </ul>
    </div>
</template>
<script>
export default {
    validate({ redirect,params }) {
        if(/[0-9]+/.test(params.id)) return true;
        return redirect('/articles')
    },
    async asyncData({ store,$content, params }) {
        const count = await $content('articles').only('title').fetch();
        const current = params.id;
        if(current > Math.ceil( count.length / store.state.indexPerPage)) redirect('/articles');
        const from = store.state.indexPerPage * (params.id - 1);
        const to = store.state.indexPerPage * params.id;
        const content = await $content('articles')
        .only(['title','path'])
        .sortBy('createdAt', 'desc')
        .skip(from).limit(to)
        .fetch();
        return {
            content,
            current,
            count:count.length
        }
    },
    computed:{
        max(){
            return Math.ceil( this.$route.params.id / 15);
        },
        num(){
            let tmp = [];
            for(let n=1; n<=this.max;n++){
                if(n == 1 || n == this.max){
                    tmp.push({pg:true,num:n});
                    continue;
                }
                if((this.current - 2 <= n) && (n <= this.current + 2)){
                    tmp.push({pg:true,num:n})
                    continue;
                }
                if((this.current - 2 - 1 == n) || (n == this.current + 2 + 1) ){
                   tmp.push({pg:false,num:"..."})
                    continue;
                }
            }
            return tmp;
        }
    },
}
</script>
詳細を解説します。
/arciles/page/{id}において{id}が数値のみ許可するようにします。そこでNuxt SSRではvalidate()というものを使用できます。params.idで{id}の値が取得できますので、そこで数値であることを確認します。もしそうでなければ、1ページ目にリダイレクトさせます。
数値であっても提供するページを超えた数を指定されては困ります。その時のために{id}のページ数が存在するかをチェックしておきます。もし存在しなければ1ページ目にリダイレクトさせます。
if(current > Math.ceil( count.length / store.state.indexPerPage)) redirect('/articles');
これはページングを実装するために必要なロジックのな話になるので、一部省略しますが必要な値は
が必要となります。ソースでは以下のように使用しています。
const from = store.state.indexPerPage * (params.id - 1);     //何件目から
const to = store.state.indexPerPage * params.id;             //何件目まで
const content = await $content('articles')
.only(['title','path'])
.sortBy('createdAt', 'desc')
.skip(from).limit(to)   // ページング取得
.fetch();
params.idが現在のページ数となっていますので、それを参考にしてページングによるコンテンツ取得をします。
asyncData()での設定は以上でOKです。params.idから現在ページ数を用いてページングのレンダーを構築します。私のページングでは
という仕様で実装されています。詳細な仕組みは上記のソースをみてください。必要分のページのリンクを作成してページングは完成です。
ページングの設定して次は静的書き出しを行います。ただし前回のようにルートをnuxtに伝えるということは不要です。どうやら/articles/indexを書き出す時にページングのnuxt-linkを辿ってルートを解決してくれているそうです。実際の書き出しでも
✔ Generated route "/articles/page
✔ Generated route "/articles/page/1" 
✔ Generated route "/articles/page/2" 
以上のようなログが確認できました。
以上で一覧ページの作成とページングが実装終了しました。ページングとページリストはコンポーネント化しておいた方が後々の開発が楽になります。次回はカテゴリーとタグ機能の解説を行います。
コメント
コメント読み込み中..