このサイトは、只今WEB業界で活躍中のデザイナー、プログラマーの方々の情報を集めたweb統合情報サイトです。

web帳

記事詳細

2017.02.21

Vue.js が予想以上に良かったので、既存WordPressに導入。Vue.js (vue-class-component) + TypeScript + WordPress で作る、記事読み込み component 「環境構築編」

どもです。

タイトル長めになりました。

かれこれ、6、7年放置気味のWordPressのサイトを サーバー移転に伴い色々と整理しています。

既存のWordPressは、がっつりプラグインに依存しているしjQueryも7年前ぐらいに入れっぱなし。。

という事で、外せるプラグインは外して、これを機にjQueryも外そうと。

したところ、よくある「さらに記事を読み込む」ボタンの実装がどうやら必要だ。

このボタンを押すと、Ajaxで記事を取得してきて、表示する。と言った機能。

今まで、プラグインを利用してきたのですが、外せるなら外して自作したいですよね。

 

そこで、どうせなら新しめのフレームワークを入れちゃおうかなと、どれを入れようか20秒ほど考えました。

Angular2を入れるにはちょっと大げさすぎるし、気軽な感じでMVVMしたいからReactはちょっと違うかなと。

という事で最近、話題の「Vue JS」の出番ですよ!!

バージョンが2.0になったということで何やら、評判も上々。

 

かなりの人気を博しておりますね。

公式ページ

https://jp.vuejs.org/

 

基本的な使い方は公式ページを参照ください ><

 

で、使って行きたいのですが、やはり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で受取り 記事を更新していく箇所を書いていければと思いますー

ではでは。

続き
Vue.js が予想以上に良かったので、既存WordPressに導入。Vue.js (vue-class-component) + TypeScript + WordPress で作る、記事読み込み component 「実装編」
 

  • RSSを登録する

  • follow us in feedly

Graphical FrontEnd Engineer
- Daisuke Takayama

MAD CITY 北九州市で生まれ育つ。20代はバンド活動に明け暮れ、ふと「webデザイナーになりたい。」と思い、デジタルハリウッド福岡校入学。卒業後、数々の賞を受賞、web業界をざわつかせる。
現在、港区六本木で活動中。

WEBデザイナーの、WEBデザイナーによる、WEBデザイナーの為のサイト。「みんなで書こう!」と仲間を募ってみたが、結局書くのは自分だけとなってしまいました。日々のメモを綴っていきます。