Vue.js + Vuex + Railsでツイッター風のSPAを作った



やりたかった事は

・Vue.jsの基本的な操作おさらい
RailsAPIのみに専念させる
・郵便番号から住所を取得するメソッド
・ユーザーに何かしらの数値を持たせて管理する

ただ作っているうちにユーザー認証をfirebaseでやったり、Vueのバケツリレーが多くなってピタゴラスイッチみたいになってきたのでVuexを導入したりと、結局未知のタスクが6割くらいになりました。



作ったもの


https://mysterious-castle-30508.herokuapp.com

GitHub - naito0106/PotatoSNS: 自作4つ目 Twitter風の投稿サイト



f:id:naito-coding0322:20181230233554p:plain

Twitterのような感じで、ユーザーがポストを投稿するアプリケーションです。
何かしらの数値の管理がしたくてウンウン唸った結果、フライドポテトの食べ過ぎを抑制すべく食べたらその旨と個数を投稿するSNS?になりました。
次こそはもうちょっと人様に発表するとき恥ずかしくないものを作ろうと思います。



個々の記事


Rails + Vue で単一コンポーネントファイルを使う
Vuexの導入
Vue.jsにfirebaseでユーザー認証を導入
VuexのデータをlocalStorageに保存する(ログイン状態の保持)
JavaScriptで今週の月曜日と日曜日の年月日+曜日 を取得するメソッド
v-bind:classとcomputed
create→ROLLBACKの原因を知りたい
Mixed Content(混合コンテンツ)エラー



全体の設計


モデル

・Userモデル

登録情報の他に、Userに紐づく各PostのPotatoの数の合計を持つpotato_countカラムをcounter_cultureで追加


・Iconモデル

今回アイコンの画像をユーザーによる設定ではなく、あらかじめ用意した8種類の中から選択する形を取った。
Icon has_many :users, User belongs_to :iconというリレーションになっている。


・Limitモデル

ユーザーが週ごとに食べて良いポテトの数をLimitオブジェクトとして作成する。 User has_one :limit

・Postモデル

投稿データ Post belongs_to :user, User has_many :posts


・Potatoモデル

各Postに紐づく食べたポテトの数を:conv_m_sizeカラムとして持つ



コントローラー

今回Rails側はAPIの処理のみにした。


API::Userコントローラー

create, show, updateの他に、Userに紐づく全Postのポテト数を@total_potato、今週中に投稿されたUserのPostのポテト数を@week_potatoとして返すget_potatoアクションを作成


API::Postsコントローラー

入力されたtextとポテトの数から、Postオブジェクトとそれに紐づくPotatoオブジェクトを作成するcreate
全てのPostを返すindex_global、特定のユーザーのPostを返すindex_userの3つを作成


API::Limitコントローラー

Limitオブジェクト自体はUser#createで値="nil"で作成される。
ここではupdateアクションのみを作成



全体の動き


f:id:naito-coding0322:20181230233554p:plain
非ログイン時の画面
完全にシングルページアプリケーションなので、ヘッダー以下のメインとなる部分にrouter-viewを差し込んでいる。
2カラム構成にしてコンポーネントで構成。


f:id:naito-coding0322:20181230233657p:plain
サインアップ画面
ユーザー認証を実装するにあたり、今まで使っていたRailsのDeviseだとVueから操作するのがややこしかったのでfirebaseを試してみた。
メールアドレスでの認証を行うのみで、その他のRailsのデータはサインアップ時にコントローラーでemailからUserを検索して処理している。


f:id:naito-coding0322:20181230233755p:plain
プロフィール登録画面
郵便番号から住所を取得する処理に少し手間取った。(別記事に詳細あり)
この処理がやりたかったので住所を登録できるようにしたが、必須ではないし意味はない。



f:id:naito-coding0322:20181230234724p:plain
アイコン選択モーダル
Iconモデルのオブジェクトを選択して各Userと結びつける形を取った。
モーダルの中身はv-showで目的に合わせて切り替えている。
v-ifは結果に応じてDOM要素を追加/削除するので、スタイルのdisplayプロパティを変更することで表示を切り替えるv-showの方がレンダリングのコストが低くて済む。
逆に、非ログイン時のみ表示するような表示の有無が頻繁に切り替わらないものはv-ifを使う。



f:id:naito-coding0322:20181230233507p:plain
ホーム画面
全ポストが最新のものから並ぶ。
ポスト毎に投稿からの経過時間が表示されているが、これはpostのcreated_atと現時刻との差分を出して経過時間の大小によって処理している。
各ユーザーのポストの右下にはそのPostに紐付いたPotatoのconv_m_sizeカラムの値が表示される。



f:id:naito-coding0322:20181230233913p:plain
投稿モーダル
サイズをS/M/Lから選べるようにしているが、JS内でMサイズでの個数に換算してからコントローラーに送りconv_m_sizeとしてオブジェクト作成している。
Sサイズ2つでMサイズ1つ、Lサイズ1つでMサイズ2つに換算。
もちろんポテトの個数がなくても投稿可能。


f:id:naito-coding0322:20181230234116p:plain
ユーザー詳細画面
クリックしたユーザーのPostを再取得して表示している。
プロフィール欄も同様。
LimitモデルのオブジェクトとしてUserが持つlimit_this_weekと、今週の日付で絞って取得したUserのポテトの個数、Userの全てのPotatoの総計が表示される。
算出プロパティとv-bind:classを使って、Limitが未設定の場合は「未設定」、今週のポテト数がLimitを上回っていれば赤文字で表示される。
また、ログイン中のユーザー本人の場合はクリックでLimit設定モーダルが開くようになっている。


作業日程


2週間

デザインと仕様設計を前回より入念にやったのでもっと早く終えられると見込んでいたけど、途中でvuexでの状態管理にリニューアルしたりして結構かかってしまった。


感想


  • とりあえず年内に完成してよかったが、いくつか残っている課題がある。
    削除機能の実装と、firebaseのAPIキーの処理、あとはstateの状態がおかしくなるので登録ページなどにURL入力で飛べてしまわないようにする。
    あとポテト数の「今週」の定義が微妙(たぶんタイムゾーンが原因)で今(日曜日の23:30)投稿したPostが計算に含まれてないので直す。

  • アプリケーションを作ってから最後にまとめてブログを書くのではなく、各タスクをやりながら詰まったところや手応えのあるところをリアルタイムにまとめていったほうが良いかもしれない。

  • postgresqlのアレコレで何度か詰まったのでDBとSQLについて、できれば仕組みというか存在意義から理解したい。なにかいい本を探す。

  • JSの基本知識/アルゴリズム的な部分を強化したい。paizaをやろう。