読者です 読者をやめる 読者になる 読者になる

Vue.js + ElectronでTwitterクライアントを作った

Vue.jsとElectronでシンプルなTwitterクライアントを作った。

きっかけとしては一言で言うとフロントエンド周りの勉強。

2017年1~3月の振り返りにも書いた通り。

自分はモダンなフロントエンドの開発に疎かったので重い腰を上げてそろそろやるかという気持ちになって3月中頃くらいからはじめた。

Vue.jsについては2,3週間前くらいから触りはじめている。

Vue.jsはちゃんとしたSPAからフォームにちょいと味付けみたいな使い方もできたり小回りがきく。

個人的にはReactでの開発よりやりたいことが直感的に書ける気がしてるので使いやすいという印象。

とりあえず最初はTODOアプリやAPIを叩く小さなフィードを作って、次にNuxt.jsでSSRできるミニサイトを作ったりで大体使い方は覚えてきたというステータス。

ライブラリでいうとvuex,vue-router,axiosをちょっと触れるようになったくらいのレベル感。

ルール

大体何か作るときにはいくつか条件を決めることにしてる。

今回の場合は、以下の3つのルール。

  • 1週間でできるとこまでやる(実際はちょっと過ぎた)
  • 自分の使わない機能は作らない
  • Vue.jsの機能を色々使う

期限と削る条件を決めておくとダラダラ開発しつづけるのを防げる。

仕様

一口にTwitterクライアントといってもAPIは大量にあるし、作ろうと思えばいくらでも作れる機能はあるので、まずは何をできるTwitterクライアントなのかを決めるのが大切になる。

今回は最強のTwitterクライアント戦争情報を参考に下記のような機能を付けることにした。

  • タイムラインにツイートを表示
    • 保持するタイムラインのツイート数に制限を設ける
    • 過去をローディングで遡れるようにはしない
  • リストが表示できる
  • リストのツイートが表示できる
  • メンションを表示
  • 通知が表示できる
  • 検索できる
  • プロフィールを表示できる
  • ツイートできる
    • 画像の非同期ローディング
    • 複数画像アップロード
  • follow/unfollowできる
  • リツイートできる
  • ファボできる
  • リプライできる
  • リンクをクリックしたらブラウザで開ける
  • メディア(静止画/gif/動画)が表示できる
  • リアルタイム更新(streaming)
  • リアルタイム更新(pooling)
  • ログインができる
  • ユーザーデータを永続化
  • ログアウトができる

開発の話

デスクトップアプリにするのでVue.jsとElectronを使うのだが、今回はelectron-vueでアプリ構成を作った。

electron-vueを使うとwebpackでビルドからelectronでのパッケージングまで設定してくれる開発環境をコマンド一つで作ってくれるので楽したい人にはとても良い。

開発の流れ

今回は状態が大量に発生することが予想されたので最初からVuexを使うことにした。

VuexはReactでいうところのReduxにあたるライブラリ。

Vuexでの状態変更は下記のような手順で行われる。

vue(イベント)→action→mutation→state

mutationがReduxのreducerなのかな。

というわけで基本的な開発の流れは、

①components配下にUIを書く

②UIにイベントハンドラを付ける

イベントハンドラからactionの関数を叩く

④actionの関数内で色々処理したりする

⑤状態変更をするならstateとそれを変更するmutationの関数を作る

⑥actionからmutationの関数を叩く

という感じで機能を作っていくことになる。

UI

UIについては普段から使い慣れてるTweetDeckに似せることにした。

似せると言っても色合いやサイドバーとかの配置だけだけど。

基本構成は、サイドバーにコンテンツの切り替えボタンとツイートをするためのボタンを置き、メインのフィードにコンテンツを表示させるという感じになる。

各機能

機能ごとに簡単な作り方と感想を書いてく。

タイムラインにツイートを表示

フィード用のstateを一つ用意(state.tweets.items)。

基本はGET statuses/home_timelineを叩いてstate.tweets.itemsに突っ込むだけ。

  • 保持するタイムラインのツイート数に制限を設ける

state.tweets.itemsに突っ込めるアイテムに上限をつけるだけ。

  • 画像の非同期ローディング

vue-loadingを使う。

リストが表示できる

GET lists/listを叩く。

サイドバーのリストボタンがクリックされたらポップアップで一覧を選択できるようにする。

リストのツイートが表示できる

GET lists/statusesを叩く。

あとは[タイムラインにツイートを表示]と同じ。

メンションを表示

GET statuses/mentions_timelineを叩く。

あとは[タイムラインにツイートを表示]と同じ。

検索できる

基本はUser Streamのstreaming apiにあるfilterに検索クエリを投げて取得した情報を表示。

あとは[タイムラインにツイートを表示]と同じ。

(自分と自分のツイートに対する)通知が表示できる

state.notification.itemsを用意。

裏側で動かしておく。

基本はUser Streamのstreaming apiで取得した情報を表示。

Favorite,Followはstreaming apiで取得したままの情報を使う。

Replyの情報はstreaming apiのtweetのイベントをパースして自分へのリプライだったら通知させる。

Retweetはstreaming apiで取得できないのでREST APIを定期的に叩く。かなり煩雑になる…。

GET statuses/retweets_of_meでRTを取得

GET statuses/retweeters/idsで各RTをしたユーザーのidを取得

GET users/lookupで取得したユーザーidからユーザー情報を取得

API叩きまくってしまうのでRTをたくさんされるとやばいと思う。

プロフィールを表示できる

GET users/showを叩く。

取得したプロフィールはツイートのユーザーアイコンをクリックされたらモーダルで表示されるようにする。

ツイートできる

POST statuses/updateを叩く。

  • 複数画像アップロード

POST media/uploadを叩く。

複数枚アップロードを実現するためにPromise+loop+非同期処理を書く必要がある。

とりあえずPromise内に非同期処理をラップしたPromiseの関数を作ってそれを再帰的に呼び出すみたいな処理を書いてしのいだが、nestキツいのでもっと良い書き方教えて欲しい。

follow/unfollowできる

POST friendships/createを叩いてフォロー。

POST friendships/destroyを叩いてアンフォロー。

リツイートできる

POST statuses/retweet/idリツイートしたいツイートのidを付与して叩く。

ファボできる

POST favorites/createにファボしたいツイートのidを付与して叩く。

リプライできる

@付きでツイートするだけ。

リンクをクリックしたらブラウザで開ける

electronのshellライブラリのopenExternal(url)を使う

メディア(静止画/gif/動画)が表示できる

先述したが、vue-loadingを使う。これを使わないと動画やgifアニメが沢山表示されたときにアプリが死ぬ。

リアルタイム更新(streaming)

streaming apiを使う。

リアルタイム更新(pooling)

setIntervalでAPI制限に引っかからない程度にAPIを叩く。

ログインができる

r7kamura氏のsrc/browser/authentication-window.jsをまるっと見て理解してやる。

ユーザーデータを永続化

ローカルのjsonファイルを使う。

jsonファイルへのset,getのインターフェースを持ったStore.jsを作って保存したい情報を入れとく。

今回はユーザーのtokenとユーザープロフィールをいれた。

ログアウトができる

時間なくてできなかった。ほぼログアウトしないのであとで。

その他

  • tweetdeckのようなnofificationは作れない。既存のAPIでは容易には無理。諦めろ。
  • 非同期処理はPromiseでラップするとなにかと便利
  • EventEmmiterを使うと、非同期処理が終了→次の処理→次の処理とパイプのようにつなげられるので重宝する
  • twitterapiを叩くライブラリはnode-twitterより、twitのほうが良い。node-twitterはstreming apiを起動したら止められない。
  • electronのmainプロセス側でbabelが必要な処理を書いたら、packagingをするときにapp/配下でもbabel-runtimeが必要になるので注意

まとめ

  • Vue.js+ElectronでTwitterクライアントアプリを作った
  • Vue.jsでの基本的な開発の流れを理解できた
  • Twitter APIについて少し詳しくなれた

この2,3週間でモダンなフロントエンドでの開発について良い感じに学べることができ、月並みだがTwitterクライアントという成果物も出せたのでなかなか楽しかった。ただテスト周りが書けてないのでそこはまたあとで。

Vue.jsについてはReactに比べてまだまだ日本で利用してる人は多くないかもしれないが、実際のところ難易度的にNext jQueryになりえるのはVue.jsだろうなという気がしてるのでこれからもウォッチしていきたい。

ちなみに今回はソースを公開することを前提に書いてたのでコードは変に抽象化したりせず冗長に書いてある。なので初心者の人には割と処理の流れが理解しやすいコードになってると思うので似たようなことやりたい人には参考になるかも。

何かあればこちらまで →@razokulover

エンジニアの採用もやってます。興味あれば声かけて下さい。 →https://www.green-japan.com/job/48124