追加読み込みした配列をvue.jsでリストレンダリングする

WordPressサイトの記事一覧（アーカイブ）ページで記事を追加読み込みしたいという要望がありました。
WP REST APIを用いてajax通信し、取得したデータをVue.jsで描画することで対応してみました。

# 実現したい内容
- 配列のデータをvueで繰り返し要素として描画したい
- 一回目のvue描画はWP側でscriptタグ内に出力した配列を使う
- 二回目以降はajax(WP Rest API)で追加読み込みしたデータを描画

# 環境
- WordPressサイト
- Vue.js　v2.3.3
- jQuery　v3.2.1

# 対応
## 一回目の描画
### 描画したい配列
WordPressのwp_footerをフックにして`</body>`付近に下記を挿入しました。

```archive.php
<script>
var posts = [
  {
    "post_id":"記事ID",
    "thumbnail":"画像へのパス",
    "date":"日付",
    "title":"タイトル",
    "url":"記事詳細ページのURL"
  },
  //続く…
]
</script>
```
（参考：プラグイン API/アクションフック一覧/wp footer
https://wpdocs.osdn.jp/%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3_API/%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%83%83%E3%82%AF%E4%B8%80%E8%A6%A7/wp_footer）


### Vueコンポーネントの登録
まずvue側でコンポーネントを登録します。
複数回vue描画がされるのでテンプレートはjs側にあった方が便利そう、との理由でこのような書き方をしました！

```posts.js
var item_tmpl = Vue.component('post-item', {
  props:[
    'post_id',
    'thumbnail',
    'date',
    'title',
    'url',
  ],
  template: 
  `
    <article class="article" :data-id="post_id">
      <a :href="url">
        <div class="article_thumbnail">
          <div class="thumbnail_img"><img :src="thumbnail" alt=""></div>
        </div>
        <div class="article_data">
          <p class="date">{{date}}</p>
          <h1 class="title">{{title}}</h1>
        </div>
      </a>
    </article>
  `
})
```
追加読み込みした際に設定内容を再利用したいのでitem_tmplという名前をつけておきます！

### Vueインスタンスの生成
リストレンダリングを出力する対象となる親要素と、配列データを登録します。

```posts.js
var vm_post_list = new Vue({
  el: '#posts_container', //この要素の中に描画するリストが入る
  data: posts
})
```
### 出力先でのコンポーネント設定
php側では親要素の用意と、コンポーネントのpropsと配列の内容とを結びつけておきます。

```archive.php
<div id="posts_container">

  <article
  is="post-item"
  v-for="post in posts"
  v-bind:key="post"
  v-bind:post_id="post.post_id"
  v-bind:thumbnail="post.thumbnail"
  v-bind:date="post.date"
  v-bind:title="post.title"
  v-bind:url="post.url"
  >
  </article>

</div>
```
ここまでの対応で一回目の出力ができました！

## 追加読み込みを描画
次にWP REST APIから取得した配列を、
一回目と同じコンポーネント設定を使用して描画します。

### 描画したい配列
REST APIで取得できるデータを一回目と同じ配列の形式になるように加工しておきます。

### ajax通信
追加読み込みさせたいタイミングでajax通信します。
通信終了後、取得したデータをセットしたvueインスタンスを生成していきます。

```posts.js
$.ajax({
  type: 'GET',
  url: 'WP REST APIのURL',
  dataType: 'json',
  data: {
    per_page: 0, //一度に取得したい記事数
    offset: 0, //取得済みの記事数などを設定
    categories: 0 //カテゴリを絞る必要があればカテゴリIDを設定
  }
}).done(function(data){

  var item_tmpl_re = {
    extends: item_tmpl //一回目のvue描画で使ったコンポーネント設定を再利用
  }

  new Vue({
    el: '#posts_container',
    data: {
      posts: data //REST APIから取得したデータをセット
    }
  })

});
```
また一回目の描画の際にarchive.phpの中に記載していたコンポーネントが
消えているので、vueが始まる前に親要素の中の一番最後に入れ直します。

```posts.js
$.ajax({
  //省略
}).done(function(data){

  //親要素の中に追加
  $('#posts_container').append(`
  <article
  is="post-item"
  v-for="post in posts"
  v-bind:key="post"
  v-bind:post_id="post.post_id"
  v-bind:thumbnail="post.thumbnail"
  v-bind:date="post.date"
  v-bind:title="post.title"
  v-bind:url="post.url"
  ></article>
  `);

  var item_tmpl_re = {
    extends: item_tmpl
  }

  new Vue({
    el: '#posts_container',
    data: {
      posts: data
    }
  })

});
```
これで二回目以降のvue描画もできるようになりました！

# おまけ
## 表示の調整
### スクロール位置の調整
追加読み込み後、スクロール位置が一番最後の要素の下まで移動してしまうことがありました。
わかりづらいので、スクロール位置が変わったように見えないように調整します。

```posts.js
$.ajax({
  //省略
}).done(function(data){

  // 記事追加前のスクロール位置を保存
  var before_pos = $(window).scrollTop();

  // Vueの処理

  // 記事追加前のスクロール位置に戻す
  $(window).scrollTop(before_pos);

});
```

### 追加読み込み要素をフェードインさせる
追加されたことがわかりやすいように、vueコンポーネント登録時に描画がフェードインするよう設定しました。

```posts.js
var item_tmpl = Vue.component('post-item', {
  props:[
    'post_id',
    'thumbnail',
    'date',
    'title',
    'url',
  ],
  template: 
  `
    // transitionで囲む
    <transition name="fade" appear>
      <article class="article" :data-id="post_id">
        <a :href="url">
          <div class="article_thumbnail">
            <div class="thumbnail_img"><img :src="thumbnail" alt=""></div>
          </div>
          <div class="article_data">
            <p class="date">{{date}}</p>
            <h1 class="title">{{title}}</h1>
          </div>
        </a>
      </article>
    </transition>
  `
})
```

以上です。

今回初めてvueを使ってみました。
気になる点などありましたらぜひお知らせください:hugging::hugging:

---
##### 17/8/3追記
コンソールを確認したところ下記のエラーが表示されていました。

```
[Vue warn]: Avoid using non-primitive value as key, use string/number value instead.
```

Primitiveでない値をkeyとして使用するのは避け、代わりにstringか数値を使用してください。
とのことです。

```posts.js、archive.php
<article
is="news-item"
v-for="post in posts"
v-bind:key="post" // ダメ
v-bind:post_id="post.post_id"
v-bind:thumbnail="post.thumbnail"
v-bind:date="post.date"
v-bind:title="post.title"
v-bind:categories="post.categories"
v-bind:url="post.url"
>
</article>
```
postがオブジェクトになっていたので、下記のように書き換えました。

```posts.js、archive.php
<article
is="news-item"
v-for="post in posts"
v-bind:key="post.post_id" // OK
v-bind:post_id="post.post_id"
v-bind:thumbnail="post.thumbnail"
v-bind:date="post.date"
v-bind:title="post.title"
v-bind:categories="post.categories"
v-bind:url="post.url"
>
</article>
```
こちらでコンソールエラーを解消することができました。

---

##### 参考
コンポーネント
https://jp.vuejs.org/v2/guide/components.html

Primitive
https://developer.mozilla.org/en-US/docs/Glossary/Primitive