Vue.js が予想以上に良かったので、既存WordPressに導入。Vue.js (vue-class-component) + TypeScript + WordPress で作る、記事読み込み component 「環境構築編」
2017.02.21
この記事は最終更新日から1年以上が経過しています。
どもです。
タイトル長めになりました。
かれこれ、6、7年放置気味のWordPressのサイトを サーバー移転に伴い色々と整理しています。
既存のWordPressは、がっつりプラグインに依存しているしjQueryも7年前ぐらいに入れっぱなし。。
という事で、外せるプラグインは外して、これを機にjQueryも外そうと。
したところ、よくある「さらに記事を読み込む」ボタンの実装がどうやら必要だ。
このボタンを押すと、Ajaxで記事を取得してきて、表示する。と言った機能。
今まで、プラグインを利用してきたのですが、外せるなら外して自作したいですよね。
そこで、どうせなら新しめのフレームワークを入れちゃおうかなと、どれを入れようか20秒ほど考えました。
Angular2を入れるにはちょっと大げさすぎるし、気軽な感じでMVVMしたいからReactはちょっと違うかなと。
という事で最近、話題の「Vue JS」の出番ですよ!!
バージョンが2.0になったということで何やら、評判も上々。
かなりの人気を博しておりますね。
公式ページ
基本的な使い方は公式ページを参照ください ><
で、使って行きたいのですが、やはりTypeScriptで書いていきたい。。。
かれこれ、3年ほど書いてきましたがやはり型があるのは良いですよね。
型が必要ということも徐々に浸透しだしておりますね。
ということで、TypeScriptを使うならこれを使えと、公式も推奨している
「vue-class-component」を使うことにしました。
github
https://github.com/vuejs/vue-class-component
サンプルを見てわかるように、Compornent ディレクティブを利用することができ、Angular Likeに記述していくことができます。
(なんとなく書いていて、結果 Angularみたくなってなってしまったがw
ビルド環境はこんな感じ
Dependencies
・ Vue.js
・ vue-class-component
Dev Dependencies
・ TypeScript
・ Webpack
・ ts-loader
・ html-loader
では、早速インストール
インストール
npm install
$ npm install vue $ npm install vue-class-component $ npm install html-loader $ npm install ts-loader
TypeScriptなので、Typingsで型定義ファイルをインストールしたいのですが、嬉しいことにVueはTypeScriptにも対応していて特に別途インストールする必要もありません。
それでは、webpackのconfigを作成していきます。
webpack
webpack.config.js
"use strict"; var webpack = require('webpack'); module.exports = { entry: "./ts/index.ts", output: { filename: "bundle.js", path: "./js" }, module: { loaders: [ { test: /\.tsx?$/, loader: "ts-loader" }, { test: /\.html$/, loader: "html-loader?minimize=false" } ] }, resolve: { extensions: ["", ".ts", ".tsx", ".js"], alias: { 'vue$': 'vue/dist/vue.common.js' } }, plugins: [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] };
なんともない、ほぼwebpackのデフォルトの設定なのですが、気をつけないといけない所として、
resolve の aliiasとして「vue$ : vue/dist/vue.common.js」を定義しないといけないところですね。 ><
これがないとコンパイルが通らず、少々苦労しました orz
余り、日本語のドキュメントもないという。。
package.json
"scripts": { "webpack": "webpack", "webpack:w": "webpack --progress --colors --watch" },
なるべく最小限で行きたいので、 npm scriptsでwebpackを実行できるようにと。
tsファイル作成
エントリーポイントをindex.ts としましたので、index.tsを作成。
とその前にざっと、ディレクトリ構成を
ts/ ├──components/ ├──service/ ├──store/ ├──bus.ts ├──components.ts └──index.ts
シンプルにこのような感じとなりました。なんとなくAngular寄りなディレクトリ構造。
では、一応説明を。
「components」は、コンポーネントのtsとそれに対となるhtmlが格納されるディレクトリです。
「service」ディレクトリは、古来Angular等のアーキテクチャでもそうですが、Ajax等による通信周りの処理を格納するディレクトリです。
「store」は、Flux等とはちょっと異なりモデル層を格納するディレクトリです。
「bus.ts」は、公式でも書かれているようにpubsubの責務を持つファイルとなります。(追って詳細を)
「components.ts」は、各componentsを集約させるファイル。
「index.ts」はエントリーポイントとなるファイルとなります。
それでは、index.tsを作成していきます。
index.ts
"use strict"; import Vue = require('vue'); import { PostMoreBtn, PostList } from './components'; const app = new Vue({ el: '#app', components: { PostMoreBtn, PostList } });
今回は、「さらに記事を読み込む」ボタンの実装を行いたいので、componentsは「PostMoreBtn」と「PostList」にしました。
Vueをrequireして、各componentsをimport。
Vueオブジェクトにcomponentsを定義し、作成。変数appに代入。
components.ts
export * from './components/post-more-btn'; export * from './components/post-list';
componentsファイル側はこんな感じですね。
components作成と共に追加していく感じですね。
ま。この辺、Angularをやっている方だと、特に変わりない感じですかね。
storeの方も作成していきましょう。
store/post.ts
interface Category { readonly name: string; readonly url: string; } interface Tag { readonly name: string; readonly url: string; } export default class Post { public title: string; public date: string; public imageUrl: string; public postDetail: string; public categoryList: Category[]; public tagList: Tag[]; }
WordPressの記事をモデルとして扱いたいので、上記のような感じにしました。
(TypeScriptの詳細等は割愛させていただきます。
Service層でWordPress側のAPIと、Ajax通信して型付けしていくイメージですね。
それでは、componentsの作成を。
components/post-list.ts
import Vue = require('vue'); import Component from 'vue-class-component'; import bus from '../bus' import Post from '../store/post'; @Component({ template: require('./post-list.html') }) export class PostList extends Vue { private posts: Post[]; data(): any { return { posts: [] } } created() { bus.$on('update-post', this.onUpdatePost) } destroyed() { bus.$off('update-post', this.onUpdatePost) } onUpdatePost(posts: any): void { posts.forEach((post: any) => { this.posts.push(post); }); } }
ここで、vue-class-componentを利用するので、vue-class-componentをimportして利用します。
また、html-loaderを利用すると、componentをhtmlとして読み込み使用できますので、テンプレートは別にファイルを作成します。
busに関しては、次回追って詳細を><
なので、「created」、「destroyed」関数は飛ばして、「onUpdatePost」関数を。
といっても説明するほどでもないのですが、Ajax通信したあと、onUpdatePost関数を経てpostを保持します。
components/post-list.html
<div> <div class="post section" v-for="post in posts"> <h2 class="post-title"><a :href="post.postUrl">{{ post.title }}</a></h2> <ul> <li v-for="category in post.categoryList"><a :href="category.url" rel="category">{{ category.name }}</a></li></ul> <p>{{ post.date }}</p> <p><img :src="post.imageUrl" :alt="post.title"></p> <p>{{ post.postDetail }}</p> <p><a :href="post.postUrl"><b class="icon next"></b>続きを読む</a></p> <div class="postTag"> <p><a :href="tag.url" rel="tag">{{ tag.name }}</a><span v-if="post.tagList.length > (index + 1)">,</span></p> </div> </div>
WordPressの記事のリストとなる部分です。
ざっと、こんな感じでしょうか。。この辺は任意で。vueJSのバインドの記述などは割愛させていただきます。
ちょっと気をつける点として、ファイルの最初にvueJSのバインドを記述しているとうまく行かず、空divを囲うことで避けました。。
(この辺、どうにかならないのか。。)
それでは、「さらに記事を読み込む」ボタンの箇所を実装していきましょう。
components/post-more-btn.ts
import Vue = require('vue'); import Component from 'vue-class-component'; import PostMoreService from '../service/post-more.service'; import bus from '../bus' @Component({ props: ['nowPostNum'], template: require('./post-more-btn.html') }) export class PostMoreBtn extends Vue { private nowPostNum: number; private postMoreService: PostMoreService = new PostMoreService(); onClick (): void { this.postMoreService.fetchData(this.nowPostNum) .then((response: any) => { bus.$emit('update-post', response.data); this.nowPostNum += 10; }, (error: any) => { console.log(error); }); } }
このボタンを押すと、非同期でWordPress側のAPIと、Ajax通信したいので、PostMoreServiceのサービス層を読み込んで利用します。
props [‘nowPostNum’] で、外から記事を今何件表示しているかを受け取ります。
postMoreServiceで記事データをfetchできたら、busに通知するため bus.$emit update-postを行い、現在の記事表示「nowPostNum」を加算していきます。
components/post-more-btn.html
<div class="moreBtnBox"> <a @click="onClick">他の記事を読み込む</a> </div>
ボタン側のテンプレートとなります。
こちらはシンプルで、クリックされたら先程のonClick関数を実行します。
と、一旦はここまでで、
(この辺でもう、Angularぽくなって来てるのが、わかるかとw
意外に長くなりそうなので、次回は WordPressのAPIと連携して、PostMoreServiceで受取り 記事を更新していく箇所を書いていければと思いますー
ではでは。