WPで投稿したブログをREST APIで取得し、Vue.jsでブログ一覧を作成
技術スタック
- バックエンド
– WordPress(ブログはカスタム投稿) - フロントエンド
– Vue.js - 環境
– Vue CLI - ライブラリ
– SPA用でVue Router、Ajaxにaxios
作成ファイル
main.js
1 2 3 | import axios from 'axios' // 追加 Vue.prototype.$axios = axios // 追加 |
Vue CLIでaxiosをインストールします。
1 | npm install --save axios |
その後main.jsに上記axiosの記述を追記してください。
以降this.$axios
で使えます。
mixin.js(任意)
1 2 3 4 5 6 7 8 9 10 11 12 | export const HOST = 'https://tacs-port.tech'; export const getBlog = { methods: { getDate: date => { return date.replace(/T.*/, '') }, getExcerpt: (excerpt, num) => { return excerpt.replace(/<("[^"]*"|'[^']*'|[^'">])*>/g, '').substr(0, num)+' <span>...続きを読む</span>' }, } } |
他でも使い回したかったので、mixinにしました。
getDate
REST APIが返す投稿日の値は、
『2020-01-04T07:27:32』
の形式で、Tの後に時間が入ってきますが、今回は不要なのでT以降の文字を削除します。
getExcerpt
REST APIが返す抜粋は100文字までで、省略記号が『[…]』です。
まずは、HTMLタグが含まれているのでそれを削除し、指定の文字数のカットしたのち、『…続きを読む』と表示させます。
router/index.js
1 2 3 4 5 6 | { path: '/blog/page/:page_num', name: 'blogPaginate', component: () => import('../views/Blog.vue'), props: true, }, |
page_num
をパラメーターとしてコンポーネントに渡します。
Blog.vue
script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | <script> import { HOST, getBlog } from '@/mixin' import Pagination from '@/components/Pagination.vue' export default { data() { return{ blogList: [], total: 0, pages: 0, currentPage: 1, perPage: 8, } }, props: { page_num: String }, components: { Pagination, }, mixins: [ getBlog ], methods: { getBlogs() { this.$axios.get( HOST+'/wp-json/wp/v2/blog?_embed&per_page='+ this.perPage +'&page='+ this.currentPage ).then( response => { for( let bl of response.data ){ this.blogList.push({ title: bl.title.rendered, // タイトル excerpt: bl.excerpt.rendered, // 抜粋 date: this.getDate(bl.date), // 日付 link: bl.link.replace(HOST, ''), // パーマリンク thum: bl._embedded['wp:featuredmedia'] ? bl._embedded['wp:featuredmedia'][0].media_details.sizes.full.source_url : '/assets/images/common/noimage.png', // アイキャッチ thum_alt: bl._embedded['wp:featuredmedia'] ? bl._embedded['wp:featuredmedia'][0].alt_text : 'Noimage', // アイキャッチのalt terms: bl._embedded['wp:term'][0], // カスタム分類 }) } this.total = response.headers['x-wp-total']; // 投稿総数 this.pages = response.headers['x-wp-totalpages']; // ページ数 }) } }, created: function(){ this.currentPage = this.$route.params.page_num // ルートパラメーター代入 this.getBlogs(); // 記事取得 }, watch: { '$route'(to) { this.currentPage = to.params.page_num; // 新しいルートパラメーター代入 this.blogList = []; // 一旦空にしてから this.getBlogs(); // 記事取得 } }, } </script> |
ページャーをコンポーネントに分けたので、importとcomponentsに定義します。mixinも。
getBlogs
axiosでWP REST APIのエンドポイントにリクエストを送り結果を受け取ります。
値を組み合わせてget()
にエンドポイントを渡します。
▼ エンドポイント例
https://tacs-port.tech/wp-json/wp/v2/blog?_embed&per_page=8&page=1
blog | カスタム投稿スラッグ |
---|---|
_embed | アイキャッチ画像を取得するために必要 |
per_page | 1ページあたりの投稿数 |
page | 何ページ目か |
● 結果
返ってきた値はresponse.data
に入っているので、for
で配列:blogListに、値を追加:pushします。
REST APIで返ってくる値は下記のようなオブジェクト階層になっています。
そして、挟んだ関数などを記載します。
タイトル | title.rendered | |
---|---|---|
抜粋 | excerpt.rendered | カスタム投稿側で抜粋を有効化 |
日付 | date | mixinで用意したgetDate() 関数を挟む |
パーマリンク | link | replace(HOST, '') で絶対パスのドメイン部分を削除 |
アイキャッチ画像 | _embedded[‘wp:featuredmedia’][0].media_details.sizes.full.source_url | アイキャッチ画像が無かったらここでエラーになるので、三項演算子で_embedded[‘wp:featuredmedia’]が無かったらnoimage.pngを代入 |
アイキャッチ画像のAlt | _embedded[‘wp:featuredmedia’][0].alt_text | 同上 |
投稿総数やページ数はheader情報に入ってきます。
なので、response.dataではなく、response.headers
に入っています。
投稿総数 | x-wp-total |
---|---|
ページ数 | x-wp-totalpages |
created
で、最初の記事一覧表示、watch
で、ページネーションで遷移時の記事一覧表示です。
SPAで同じコンポーネントで値を更新する時はwatch
で再度パラメーターを代入する必要があります。
template
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <template> <div> <p class="p-post-num">{{ (currentPage -1) * perPage ? (currentPage -1) * perPage : 1 }} ~ {{ (currentPage -1) * perPage + blogList.length }}件目表示中 / {{ total }}件中</p> <h2 class="l-sh mt0">最新ブログ一覧 {{page_num}}ページ目</h2> <ul class="m-blog-archive-list"> <li v-for="(bl, i, key) of blogList" :key="key"> <router-link :to="bl.link" class="is-imghover"> <p class="m-blog-archive-list-thum"> <img :src="bl.thum" :alt="bl.thum_alt"> </p> <div class="m-blog-archive-list-inner"> <p class="l-sh02" v-html="bl.title"></p> <p class="m-list-date"><time :datetime="bl.date">{{ bl.date }}</time></p> <p class="m-blog-cat" v-for="(term, i, key) of bl.terms" :key="key"><span>{{ term.name }}</span></p> <div class="m-blog-list-body" v-html="getExcerpt(bl.excerpt, 80)"></div> </div><!--m-list-inner--> </router-link> </li> </ul><!--m-blog-archive-list--> <Pagination :current="Number(currentPage)" :pages="Number(pages)"></Pagination> </div> </template> |
あとはblogList
にしまった記事データをv-for
で出力するだけです。
ページネーションも作成したので、そのコンポーネントに値を渡します。
Pagination.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <template> <div class="m-pager"> <ul class="page-numbers"> <li v-if="current - 1"><router-link :to="'/blog/page/'+( current - 1 )"><</router-link></li> // ① <li v-for="n of pages" :key="n"><router-link :to="'/blog/page/'+n"><span :class="{ 'current' : current == n }">{{n}}</span></router-link></li> // ② <li v-if="current < pages"><router-link :to="'/blog/page/'+( current + 1 )">></router-link></li> // ③ </ul> </div> </template> <script> export default { name: 'Pagination', data() { return{ } }, props: { current: { type: Number }, pages: { type: Number }, }, } </script> |
親コンポーネントからcurrent
とpages
の値を受け取って、それに応じて出力させます。
①は、一つ前のページを出力します。(現在のページ -1)
v-ifでそれが0(1ページ目)の時は非表示にします。
②は、v-forでページ数分リンク出力を繰り返します。
spanタグに現在のページ時にcurrentというclassを付与します。
③は、一つ後のページを出力します。(現在のページ +1)
v-ifでそれがページ数より大きい場合(最後のページ)の時は非表示にします。