40
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Tumblr API + Vue.jsでTumblrテーマを作った話

Last updated at Posted at 2016-03-07

初投稿です。

先日、僕のサークルのWebサイトをリニューアルしました。サイトは更新がかんたんなのでTumblrで作っています。リニューアル前はTumblr APIを使ってReactで作っていましたが、今回はVue.jsを使って作り直しました。そのときのもろもろをメモしておきます。

作ったサイト

Dancng Girl.
[2017.9.8 追記]
結局↑のサイトはTumblrをやめてWordPress(WP REST API) + Vue.jsに移行しました。

中の人について

Webデザイナー。

なぜTumblr API + Vue.jsなのか

Tumblrのテーマタグは自由度が低い

Tumblrはテーマタグが用意されていて、テーマカスタマイズ画面でHTMLを編集し、テーマタグを埋め込んだりすることでカスタマイズが可能です。

Creating a custom HTML theme | Tumblr (日本語)

ですが、正直言って用意されているタグの種類も少なく、自由度が低いです。オリジナリティのあるWebサイトをTumblrで作りたい場合、かなり苦行だと思ってもらえれば結構です。
テーマタグを記述するHTMLでは、トップページや下層ページ、各投稿タイプなども含めて、全て1つのHTMLに記述しなければならず、コードの見通しも極めて悪いです。

また、開発時にサイトをローカルで確認するのも面倒です。MiddlemanとTumblarghというGemでTumblrテーマをローカルで開発することも可能ですが、開発が数年前で止まっているGemで、現在では非対応のタグも多く、なかなか大変です。

APIがあればなんでもできるからね

Tumblr APIを使うことで、「ローカルでTumblrのテーマタグが動かないから表示確認ができない」「1つのHTMLにすべてのタグを記述しないといけない」という問題から開放されます。自分の好きなように、サイトや記事の情報を取得し、表示すればいいだけだからです。

「小規模のサイトだったら気合で普通にテーマタグ書けば?」と言われるとその通りなので反論できませんが、それはそれ、ということで。
また、Tumblr APIもテーマタグと同じく不備が多く、欲しい情報が取れなかったりします。そこは後述します。

なぜVue.jsなのか

vue.js

  • AngularJSはチュートリアルくらいしか触ったことがない
  • Reactはいくつかサイトを作ってみたけどあまり気に入らなかった
  • Vue.jsはドキュメントも短くて読みやすくて比較的使いやすかった

という理由から、Vue.jsを使いました。

Tumblr APIを使うにあたって

今回のようにTumblr APIをブログ記事の取得にのみ使う場合、必要なのはサイトのURLとAPI Keyです。API Keyは以下のURLから取得可能です。

https://www.tumblr.com/oauth/register

この記事を参考にしたりしながら、アプリを登録してAPI Keyを取得します。

Tumblr APIを使うためにAPIkeyを取得する | Stainless Note

Vue.jsでTumblrテーマを作る

Vue.jsはそのまま使うと、AngularJSのようにHTML内にデータバインディング構文を書いていくことになります。

Vueifyとvue-loader

Vueify (with Browserify)やvue-loader (with Webpack)といったnpmパッケージを使うことで、.vueという拡張子のファイルの中に、ViewのHTMLタグおよびデータバインディング構文と、Vue.jsの処理を記述することができ、コンポーネント単位でコードを管理することができるようになります。Reactの.jsxファイルのような感じです。

また、CSSも.vueファイルの中に書くことができます。が、僕はCSSは別で管理するのが好きなのでスタイルは.vueファイルには書きません。

僕はBrowserifyをよく使うので、Vueifyを使って開発していきます。

Gulpをタスクランナーに使う

タスクランナーとして、Gulpを使います。ファイルの追加や変更を監視して、

  • Browserify + Watchfyで.js.vueファイルのコンパイル
  • Stylusのコンパイル
  • 画像のMinify

などをおこないます。
開発が一段落して実際にTumblrに反映するぞという場合は、

  • JavaScriptの圧縮
  • CSS内の画像ファイルへのパスを絶対パス(ファイルをホストしているURL)に書き換え
  • CSSの圧縮
  • HTMLにTumblrテーマタグを埋め込み(後述します)

などをおこないます。

各タスクの詳細は、GitHubのリポジトリをご覧ください。

ページ遷移時に任意の処理やアニメーションを実行する

Vue.jsのルーターであるvue-routerを導入しページ遷移を実装します。

そのときに、ページ遷移時(あるページに遷移するときやあるページから他のページに遷移するとき)に任意の処理やアニメーションを実行したい、という場合があると思います。そういった時は、vue-routerのトランジションフックを使うと便利です。

トランジションフック | vue-router ドキュメンテーション npm package

source/javascripts/pages/News.vue
<template lang='jade'>
...
</template>

<script>
export default {
  // ... 他のオプション
  route: {
    activate: function(transition) {
      if (transition.from.path === '/work') {
        console.log('作品一覧からニュース一覧に遷移したよ');
      }
      if (transition.from.name === 'post') {
        console.log('ニュース詳細からニュース一覧に遷移したよ');
      }

      transition.next();
    },
    deactivate: function(transition) {
      if (transition.to.path === '/') {
        console.log('ニュース一覧からトップページに遷移するよ');
      } else if (transition.to.name === 'post') {
        console.log('ニュース一覧からニュース詳細に遷移するよ');
      }

      transition.next();
    }
  }
};
</script>

トランジションフックは引数としてtransitionオブジェクトを受け取ります。transition.to.pathなどとすることで遷移にまつわるいろいろな情報を取得することができるので、それに応じて条件分岐して処理を実行します。

コンポーネント間でデータのやり取りをするためにVuexを使う

例えばサイトのタイトルや記事を取得する関数などは、複数のコンポーネントでほぼ同じものを使いまわします。そのときにそれぞれのコンポーネントに同じコードを書くのは、冗長だし保守性も低いと思います。
また、記事一覧コンポーネントの中である処理をして表示を変更したときに、ヘッダーコンポーネントの表示ステータスを変更したい、といったシチュエーションもあると思います。

そういった場合に使えるのがVuexです。Vue.js向けのFluxフレームワークです。
VuexおよびFluxについて僕はそこまでよく分かっていないのですが、

  • 使い回すようなStateは各コンポーネントでは持たない、Vuex側(Vuex Store)でStateを保持しておく
  • コンポーネントはVuex Store上のStateを監視・表示しているだけ
  • コンポーネントは「◯◯を実行するよ」という通知をするだけ、コンポーネントがStateを変更するわけではない
  • VuexがStateを変更するような関数を実行する
  • Stateが変わったら、監視していたコンポーネントが表示している値も変化する

という雑な理解をしています。だ、だいたい合ってるやろ……。

Vuexを使うことで、コンポーネント間でのデータのやり取りや関数の使い回しが楽になりました。

Tumblr APIは固定ページの情報が取得できない

「Tumblr APIもテーマタグと同じく不備が多く、欲しい情報が取れなかったりします」と書きましたが、個人的に一番つらいのが、固定ページの情報を取得できないことです。つらい……。

たとえば「About」などの固定ページをWebサイトに置く場合がよくあると思いますが、そういう場合はjQuery.ajax();などを使ってむりやりページのコンテンツを取得し、表示するのがおすすめです。

source/javascripts/pages/About.vue
<template lang='jade'>
.article {{{articleData}}}
</template>

<script>
window.jQuery = window.$ = require('jquery');

export default {
  // ... 他のオプション
  data() {
    return {
      articleData: {}
    };
  },

  ready() {
    jQuery.ajax({
      type: 'GET',
      url: 'http://dncngrl.com/about',
      dataType: 'html',
      timeout: 10000,
      success: (res) => {
        const article = $(res).find('#article').html();
        this.articleData = article;
      }
    });
  }
};
</script>

なかなか微妙な解決策ですが、Tumblr APIを使う場合はこれしかないかな、と思います。テンプレートにページ内容を直に書いてもいいかも知れませんが、Tumblrでは固定ページの内容はテーマカスタマイズ画面に記述します。固定ページの情報を変更した時に、テーマのテンプレートの方も編集しなければならないので、面倒です。

momentで日付をフォーマットして表示する

Tumblr APIを叩いて投稿の日時(date)をリクエストすると、2016-02-26 07:00:00 GMTというような書式のデータが取得できます。momentを使って日付のフォーマットをおこないましたが、少し詰まったのでメモしておきます。

ChromeとOperaでは、以下のコードで問題なく日付が整形されます。

import moment from 'moment';
const date = moment(new Date('2016-02-26 07:00:00 GMT')).format('YYYY.M.D');
console.log(date); // 2016.2.26

ですが、FirefoxとSafariだと、Invalid Dateという表示になり、大変厳しい感じになります。

この解決策としては、dateプロパティではなくtimestampプロパティを使います。timestampの値である1456470000といった数字はUnixタイムスタンプというもののようで、これをmomentでフォーマットすると、すべてのブラウザでいい感じに日時が表示できます。moment.unix()メソッドの引数に日付オブジェクトを入れます。

import moment from 'moment';
const date = moment.unix(new Date('1456470000')).format('YYYY.M.D');
console.log(date); // 2016.2.26

その他のTumblrテーマ開発にまつわるあれこれ

サーバーサイドレンダリングについて

ReactやVue.jsといったフレームワークは、サイト読み込み時にJavaScriptが実行され、DOMが生成されたりします。なので、$ curl hoge.comや「ページのソースを表示」でソースを表示してもbody以下が空っぽ、ということになります。

それを避けるために、サーバーサイドレンダリングをおこない、JavaScriptが実行できない環境でもサーバー側でHTMLを生成する必要があります。
ですが、今回はTumblrのテーマカスタマイズ画面にHTMLを貼り付ける前提なので、サーバーサイドの処理などはおこなうことができません。

そこで、以下の方法がおすすめです。

  • HTMLにはTumblrのテーマタグを普通のテーマ同様に記述しておく
  • 検索エンジンなどのロボットがアクセスした場合は、テーマタグが実行されて情報が表示される
  • 人間がブラウザでアクセスした場合には、JavaScriptが実行されbody以下を全て書き換えるので、テーマタグで生成された情報は見えない

HTMLにテーマタグを記述する必要はありますが、タグにClass付けやスタイリングなどは一切必要ありませんし、セマンティックにタグを記述しておくだけで良いので、お手軽だと思います。

Tumblrのテンプレートタグを本番環境でのみHTMLに流し込む

上記のサーバーサイドレンダリング用のテンプレートタグやOGPのmetaタグをHTMLに記述する必要がありますが、ローカル開発時にそれらのタグがあると、表示エラーになったりします。なのでローカルではテーマタグは一切記述せず、本番環境向けのビルド時のみテーマタグをHTMLに流し込みたいです。

Gulpを使うと簡単で、gulp-includeというパッケージを使うと可能です。

まず、HTMLとテンプレートタグは以下のような感じです。

source/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  @@if (env === 'development') { @@include('templateTag/development/meta.html') }
  @@if (env === 'production') { @@include('templateTag/production/meta.html') }
</head>
<body>
  @@if (env === 'production') { @@include('templateTag/production/template.html') }
</body>
</html>
source/templateTag/development/meta.html
<meta charset="UTF-8">
<title>Dancing Girl.</title>
<meta name="description" content="description here.">
<meta name="viewport" content="width=device-width, maximum-scale=1.0">
source/templateTag/production/meta.html
<meta charset="UTF-8">
<title>{block:PostSummary}{PostSummary} | {/block:PostSummary}{Title}</title>
<meta name="description" content="{MetaDescription}">
<meta name="viewport" content="width=device-width, maximum-scale=1.0">
{block:TagPage}
  <meta name="robots" content="noindex,follow">
{/block:TagPage}
{block:DayPage}
  <meta name="robots" content="noindex,follow">
{/block:DayPage}

<meta property="og:locale" content="ja_JP">
<meta property="og:description" content="{MetaDescription}">
<meta property="og:site_name" content="{Title}">
{block:IndexPage}
  <meta property="og:title" content="{Title}">
  <meta property="og:image" content="{image:Ogp}">
  <meta property="og:type" content="website">
  <meta property="og:url" content="{BlogURL}">
  <meta meta name="twitter:card" value="summary">
{/block:IndexPage}
{block:PermalinkPage}
  {block:Posts}
    {block:Text}
      <meta property="og:title" content="{block:PostSummary}{PostSummary} | {/block:PostSummary}{Title}">
    {/block:Text}
    {block:Photo}
      {block:Date}
        <meta property="og:title" content="{Year}.{MonthNumberWithZero}.{DayOfMonthWithZero} | {Title}">
      {/block:Date}
      <meta property="og:image" content="{PhotoURL-HighRes}">
    {/block:Photo}
    {block:Photoset}
      {block:Date}
        <meta property="og:title" content="{Year}.{MonthNumberWithZero}.{DayOfMonthWithZero} | {Title}">
      {/block:Date}
      {block:Photos}
        <meta property="og:image" content="{PhotoURL-HighRes}">
      {/block:Photos}
    {/block:Photoset}
  {/block:Posts}
  <meta property="og:type" content="article">
  <meta property="og:url" content="{Permalink}">
  <meta meta name="twitter:card" value="summary_large_image">
{/block:PermalinkPage}

<link rel="shortcut icon" href="{Favicon}">
<link rel="alternate" type="application/rss+xml" href="{RSS}">
<link rel="apple-touch-icon" href="{PortraitURL-128}">

Gulpのタスクは以下です。秘伝のGulpfileなので、謎にCoffeeScript……。

gulp/tasks/include.coffee
gulp    = require 'gulp'
include = require 'gulp-file-include'

gulp.task 'include:development', ->
  gulp
    .src 'source/index.html'
    .pipe include
      prefix: '@@'
      basePath: '@file'
      context:
        env: 'development'
    .pipe gulp.dest 'build/'

gulp.task 'include:production', ->
  gulp
    .src 'source/index.html'
    .pipe include
      prefix: '@@'
      basePath: '@file'
      context:
        env: 'production'
    .pipe gulp.dest 'build/'

これで、$ gulp includeを実行すると、index.htmlにHTMLが流し込まれます。

CSSの画像のパスを変更する

Tumblrはテーマに必要な画像などのファイルをアップロードすることもできますが、使い勝手がよくありません。自分のサーバなどでファイルをホストするのがおすすめです。

ローカル開発時にはもちろん画像などはローカルにあるでしょうから、開発時はローカルのファイルをCSSから参照し、本番環境では外部サイトに置いたファイルをホストします。GulpでCSSに記述してあるパスを変換してあげると良いです。gulp-replaceを使うとかんたんに可能です。

source/assets/stylesheets/style.styl
.index_title
  background url('../images/index_logo.png') no-repeat
gulp/tasks/stylus.coffee
gulp = require 'gulp'
stylus = require 'gulp-stylus'
replace = require 'gulp-replace'

gulp.task 'stylus', ->
  gulp
    .src 'source/assets/stylesheets/**/*.styl'
    .pipe stylus()
    .pipe replace '../', 'http://example.com/'

これで、$ gulp stylusを実行すると、パスが置換された以下のようなCSSが出力されます。

build/assets/stylesheets/style.css
.index_title {
  background: url('http://example.com/images/index_logo.png') no-repeat;
}

終わりに

以上、Tumblr APIとVue.jsでTumblrテーマを作る場合の勘所のまとめ的な投稿でした。かなり長くなってしまい、申し訳ありません。

サーバーサイドレンダリング(をしないでテーマタグを記述する)や、Gulpのタスクなどは、Vue.js以外のフレームワークでTumblrサイトを作る場合にも使えると思います。

「けっこう面倒くさい感じだし、いやホント普通にテーマタグ書けば?」と思われた方、実にその通りです。
でも、今回制作したサイトのようにシームレスな遷移を実現したいという場合、また例えば制作実績はWordPressで管理したいけどブログはTumblrでやりたい、などの要望がある場合は、APIを使ってJavaScriptフレームワークでサイトを作るのが、自由度が高くて良いと思います。

ここ最近はずっとTumblr APIでTumblrサイトばっかり作っていたので、次はWP REST APIを使って自分のWordPressのサイトをリニューアルする、などをやりたいと思います。

40
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?