Nuxt.js + firebaseで「積ん読防止」アプリを作ってみた
この記事は「JX通信社Advent Calendar 2019」の14日目の記事です。
はじめに
こんにちは。JX通信社でエンジニアインターンシップをしているペイと申します。
1ヶ月ほど前からKI-develop1で積ん読を防止できるアプリBookMotionを開発しています。
今回はBookMotionでWebフロントとfirebaseを担当したので作っていて良かったこと(主にWebフロント)など紹介できればと思います。
開発環境や具体的なコードの記述はないので知らなかったことが知れると思ってお読みください。
作ったもの
ランディングページ / BookMotion WEB(β版)
BookMotionは積ん読を防止する為に、読みたい本を簡単に登録、
読書のモチベーションを維持することできるようになっているアプリです。
特にエンジニアの方は,技術書を沢山積んでいるのではないでしょうか?
BookMotionはweb版でβ版として一般公開, iOS, Androidは準備中です。
WEB版の様子
積み本のリストを表示している画面。
読書開始日、読書終了予定日を設定することができます。(モバイル版ではpush通知を予定)
積みたい本、気になる本をデータベースから検索している画面
スマホ表示
使った技術
フロントエンド
Nuxt.js (v2.9.2)
- Vueのフレームワーク
- 今回はSPAモードで使用
TypeScript(vue-property-decorator)
- VueをTypeScriptで書くためにvue-property-decorator使用
Atomic Design
- iView UI
- UIフレームワーク
- JX通信社Advent Calendar 2019 7日目の記事 で 渡辺さんが紹介しています。
インフラ
- Firebase Authentication
- 認証に使用
- Cloud Firestore
- クラウドホストの NoSQL データベース
- 登録した本のデータなどを保存する際に使用
- Cloud Functions for Firebase
- OCRから本のタイトル等を抽出しオーバーレイした画像を返す
- Slackに利用額を通知する際に使用
- Google Cloud Vision API
- 本のタイトルなどを検出・抽出する際に使用
実装していて良かったこと
Nuxt(SPA)のここが良い
前提: SPAモードで実装した場合の話をしています。
1. middlewareが便利
Vueを触っている人なら一度は見たことがある、ライフサイクルの図があると思います。
vueではインスタンスが生成されてからDOMが構築されるまでに順序があります。
例えば頻繁に使用される、createdはインスタンスが生成された後に実行される為、コンポーネントが呼ばれて最初に走らせたい処理などを呼ぶことができます。
しかし、インスタンスが生成されてから、API等でサーバーにリクエストする時、ログインの情報からドメインとなる情報まで複数持ってくるのは難しいです。
キャッシュさせたり、ローカルストレージに保存する方法などもあると思いますが、ブラウザ上でデータを持っておくのはあまり好ましくなかったりします。
そこでコンポーネントが呼ばれる(インスタンスが生成される)前にミドルウェアを挟んで、そこでログインの状態などがわかると便利じゃないですか?()
Nuxt.jsではmiddleware が使えます。実装の方法によっては本当に便利です。
実装
middlewareの処理を書く
middlewareディレクトリの配下に任意のファイルを作成
今回はauthenticated.js
というファイルで作成する
export default function({ route, redirect, store }) { /** 任意の処理 route.nameでpathを取得できる store.state.hoge のようにvuexにアクセス可能 redirect('/login')でリダイレクトしてくれる */ }
nuxt.config.jsにmiddlewareの名前追加する
middleware/authenticated.jsというファイル名の場合は、以下のように記述します。
authenticatedはミドルウェアのファイル名によって変わるので適宜変更してください。
export default { mode: 'spa', // ~省略~ router: { middleware: ['authenticated'] } }
firebaseでのログインを実装する場合は以下の記述で現在ログインしているユーザーを取得することができるので実装の方法によってはミドルウェアに挟めてログインの状態を各ページが呼ばれる前に確認することができます。
import { auth } from '~/plugins/firebase' auth.onAuthStateChanged((user) => {})
2. pluginを使ってモジュールを呼び出せる
NuxtのpluginはNuxt.jsで作っているインスタンスが生成される前に呼ばれるので外部モジュールなどを呼ぶ時に便利です。
上記で紹介したmiddlewareは各ページが呼ばれる前に実行したい関数がある場合などが適していますが、
pluginはルートのアプリケーションがインスタンス化される前に実行されるのでページ転移する度に関数を実行したいという時には適していません。
例えばfirebaseのイニシャライズ処理をしたい時などにおすすめです。
import * as firebase from 'firebase/app' import config from '~/firebase.config' if (!firebase.apps.length) { firebase.initializeApp(config) } export const auth = firebase.auth()
また、全てのVueコンポーネントで特定の関数を使用することができます。 下記はインスタンスへ注入する場合です。
import Vue from 'vue' Vue.prototype.$hoge = foo => console.log(foo)
export default { mounted () { this.$hoge('hello') } }
またNuxtにはInject
という機能があり、共通の関数を定義することができます。
Injectを使ってDIをしている方の記事も上がっているので興味がある方は調べてみてください。
vue-property-decoratorを使ってコードを見やすく
VueをTypeScriptで書く為のライブラリです。
jsで書くVueでは、Propsなどを除いて型安全ではないで今回使いました。
また、素のVueは,
カンマが多く登場するので、コードの見通してもよくありません。
以下jsで書いた場合
export default { data: () => ({ hoge: false, }), computed: { foo: { get() { return ['menu-icon', this.hoge ? 'rotate-icon' : ''] } }, }, created() { this.hoge2() }, methods: { bar() { this.$refs.foo2 } } }
以下はvue-property-decoratorを使って上記のコードをリファクタしたコードになります。
<script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator' @Component export default class Index extends Vue { hoge: boolean = false get foo(): Array<string> { return ['menu-icon', this.hoge ? 'rotate-icon' : ''] } created() { this.hoge2() } bar(): void { this.$refs.foo2 } }
無駄な,
がなく、スッキリしたコードになりました。また、戻り値を書くことで型安全になりました。
特にAPIを叩いてデータを流し込む際は型がないと、正しい値を入っていると保証できないので、型をしっかり定義してあげることは重要です...
詳しい書き方はvue-property-decoratorで調べると記事が結構出てくるので調べてみてください。
Firebase UIで認証の見た目を実装
今回はログイン認証をFirebase Authenticationを使って実装しました。
Vueで実装する場合はとても簡単でFirebase UIのプラグインを読み込んで,インスタンスを生成したらtemplate内のタグに埋め込むだけで使用できます。
公式のページを読むだけで実装できたので、機能の開発に集中したい時や、ログインの実装を任せたい時はおすすめです。
現状の課題
Atomic Designの粒度があっているのか分からない。
現状Atomic Designもどきで実装しています。
Atomic Designは機能によって粒度を分けるのですが、moleculesとorganismsの境目が本当に正しいのかどうか、
再利用性の低いコンポーネントをわざわざmoleculesに分ける必要があるのか、
など気になる点が多くあり、今後の課題です。
今後について
webフロントでは実装が増えるごとに手動でのデザインの確認やロジックの確認をすることに限界を感じているのでそろそろテスト導入していかないといけないなーって感じています。
また、コンポーネントが増える毎に自分が実装したAtomic Designに幻滅するのでリファクタを重ねていかないとなーって感じです。
さいごに
firebaseを使うことで、各自メインの機能を作ることに集中できました。
Firestoreのデータモデルに最初は苦戦しましたが使ってみるとRDBより楽なのでは?と感じたりもするの色々触ってみるのも大事だなと感じました。
BookMotionはweb版でβ版として一般公開, iOS, Androidは準備中なので、積ん読しちゃっている人は是非使ってみてください!!
参考文献
https://blog.shimar.me/2018/03/31/nuxt-firebase-authentication.html
https://tech.cydas.com/entry/nuxt-inject
https://qiita.com/ryo2132/items/4d43209ea89ad1297426
-
自分を含め大学生3人でwebサービスなどを開発しているチーム名twitter.comtwitter.comtwitter.com↩
VSCodeでESLintを使う
はじめに
VSCodeでjsやtsを書いていましたが、ESLintを無視して自動的に修正される謎現象に苛立っていたのでこの記事を書いています。
今回はVSCode上でESLintのルールに沿ったjsとVueファイルを自動的に修正することを目標にします。
VSCodeの設定
私のようになぜかESLintを無視して、自動的に修正されるようでしたらVSCodeをアンインストールしてください。私の場合ココを参考にしてVS Codeに関連する設定やキャッシュを削除したら謎現象は止まりました。
そしたら、VSCodeのマーケットプレイスでこれをインストールします。
ファイルを保存したらESLintの構文チェックが自動的に修正してほしいのでVSCodeのsettings.jsonで以下のように記述します。
{ // 参考にさせていただきました。 // https://qiita.com/moriyuu/items/6bac1c75c61d9d359f96 // https://qiita.com/neuwell/items/27ea4efee9f67b33e053 "eslint.autoFixOnSave": true, "files.associations": { "*.vue": "vue" }, "eslint.validate": [ "javascript", "javascriptreact", { "language": "vue", "autoFix": true } ] }
上記設定を書いたら、VSCodeを再起動
ESLintを使う
公式に書いていますが、グローバルにインストールすることは推奨されていません。なのでローカルにインストールします。
npm install eslint --save-dev
初期設定するためにinitします。設定について何回か質問されるので選択してください。
./node_modules/.bin/eslint --init
終わったら.eslintrc.js
が作成されていると思います。
ルールを記述する
ルールは先ほど作られた.eslintrc.js
のrules内に書いていきます。沢山のルールがあるので、とりあえず今回は文字列はシングルクォーテーション
に、セミコロンは無し
の設定を書きたいと思います。
"rules": { "semi": ["error", "never", {"beforeStatementContinuationChars": "never"}], "quotes": ["error", "single"] }
semi
,quotes
はESLintのルールの名前- off,warn,errorの三段階エラーレベルがある
とりあえずここまで設定したら、js,Vueファイルで上記のルールに沿った自動修正がされると思います。
結構ざっくりの説明なのであとは公式さんみてください。
Ruby 配列,ブロック,範囲
配列
配列の宣言
a = [1,2,3] a, b = [1,2] c, d = [10] #dはnilになる
要素に追加
a[1] = 20
末尾に追加
a << 10
要素の削除
a.delete_at(3)
引数で指定した文字を削除
a.delete(2)
ブロック
Rubyでは一般的に使われるfor文ではなくeachメソッドが使われる
number = [1,2,3,4] sum = 0 number.each do |n| sum += n end # sum=10
条件を指定して値を削除
a = [1,2,3,1,2,3] # 戻り値が真になったら削除する a.delete_if do |n| # odd?で奇数判定している n.odd? end # a = [2,2]
do...endではなく{}で囲める
numbers.each {|n| sum += n }
map
mapではブロックの戻り値で配列が作れる。空の配列を宣言して末尾に追加するなどの代わりに短くかける
numbers = [1,2,3,4,5] new_numbers = numbers.map {|n| n*2 } #new_numbers = [2,4,6,8,10]
select
戻り値が真の値だけで配列を作る
numbers = [1,2,3,4,5,6] event_numbers = numbers.select {|n| n.even? } # event_numbers = [2,4,6]
reject
戻り値が偽の値だけで配列を作る
numbers = [1,2,3,4,5,6] non_multiples_of_three = numbers.reject {|n| n%3==0} # event_numbers = [1,2,4,5]
find
戻り値が真になった最初の要素を返す
numbers = [1,2,3,4,5,6] event_number = numbers.find { |n| n.even?} # event_numbers = 2
inject
繰り返し計算する
numbers = [1,2,3,4] # ループ処理1回目はinjectの引数0がresultに入り、numbersの1がnに入りresult + nの結果1がresultに入る # ループ処理2回目はresultが上記の結果1、nが2 # その繰り返し sum = numbers.inject(0) {|result, n| result + n} # sum = 10
範囲オブジェクト
値の範囲を表す範囲オブジェクト
range = 1..10(1<=10) range2 = 1...10(1<10)
文字列の範囲を取り出す
a = 'abcdefg' a[1..3] # bcd
配列の範囲を取り出す
a = [1,2,3,4,5] a[1..3] #2,3,4
配列に直す
a = (1..10).to_a # 以下のような書き方もある a [*1..10]
参考
https://www.amazon.co.jp/exec/obidos/ASIN/4774193976/junic05-22/
ScalaのOption
Optionとは
- 値があるかもしれない時に使う
- 実行時ではなくコンパイル時に気づける(エラーを型で表現するため)
- 使用する際はOptionでラップした(Optionで囲う)メソッドなどを定義する
Optionの値
- Option[A]は抽象クラス
- Some[A]はOptionを継承したクラス
- 値がある時はOptionインスタンス、ない時はNoneオブジェクトを返す
Optionの値を使う場合
- getで取り出せるがNoneに対して取り出す場合例外になる
- getOrElseを使うとNoneの時の対応ができる
- foreachを使うとSomeの時にのみ実行してくれる
- matchを使ってSomeとNoneに対しての処理を書く
Optionの変換
- OptionのIntからStringに変換する場合はmapを使う(A => B)
Option(1).map(_.toString)
mapについての説明
- mapは変換したい場合に使用される
- _.hogeはi => iの簡略版
- 下記は同義
Seq(1,2,3).map(_ * 2) Seq(1,2,3).map(i => i*2)
PostgreSQL 基本操作
はじめに
最近HerokuとPostgreSQLの組み合わせを使うことが増えた気がするので基本的なcliの操作をまとめたいとおもいます。
環境
macOS Mojave (10.14.5)
install
brew install postgresql
起動
macを起動するたびに自動的にpostgresqlが起動する
brew services start postgresql
確認
起動状況を確認する
brew services list
停止
brew services stop postgresql
接続
ターミナル等からpostgresqlに接続します
psql -d postgres
psql の後にオプションをつけてログインします。
上記の場合だと -d
でpostgres
というデータベースを指定します。
ログイン時のオプションは
-h, --host=ホスト名 データベースサーバーのホスト -p, --port=ポート番号 データベースサーバーのポート番号(デフォルト: "5432") -U, --username=ユーザー名 データベースのユーザ名(デフォルト: "postgres") -w, --no-password パスワード入力を要求しない -W, --password パスワードプロンプトを強制表示する
psql内コマンド
psql内というのはログインした後の操作のことです。
postgresでログインしたらシェル上では以下のような形になると思います。
postgres=#
デーベース一覧
\l
ユーザー 一覧
\du
別のデータベースに接続
hogeに接続
\connect hoge
データベース作成
hogeという名前で作成
;
最後につけてください
create database hoge;
データベース削除
hogeデータベースを削除
;
同じく
drop database hoge;
postgresを抜ける
\q
さいごに
最近DB周りの勉強不足を痛感することが多々あるので、積極的に触っていこうと思います。
今回は必要最低限のコマンドしか紹介できなかったので、ロールの作成等の記事も書けたらと思います。
参照
Python3学び直してみる その3
Python3学び直してみる その2 - ペイの技術MEMO
if文
if 条件式: print('True') else: print('False')
- pass文
- 何も行わない文
- if以外でも使える
- 文が必要で何も行わない時に置く
if n > 0: print('hoge') elif n == 0: pass else: print('hogemaru')
論理型
論理型(bool型)はFalse,True
Falseの規則
- 0
- 0.0
- None
- 空の値
Trueの規則
- 上記以外
論理演算子
and演算子
pythonの場合and演算子は左のオペンランドを比較してfalseなら右オペンランドを比較しない
左がFalseなら、その値を生成し、左がTrueなら右を評価し、値を生成。
- 下記の場合
- bが0ならcはfalse
- bが0でないならcはaとbの剰余
c = b != 0 and a % b
or演算子
左がTrueなら、その値を生成し、左がFalseなら右を評価して、その値を生成。
- 下記の場合
- bが0なら評価終了
- bが0でないなら右を評価
b == 0 or print('a // b =', a // b)
not演算子
- 反転
条件演算子
- 唯一の三項演算子
- if文を凝縮した形
x if y else z
Python3学び直してみる その2
前回の続きです。
画面への表示
画面への表示はprint()
関数を使用する
print('hello python')
改行しないで表示
print('hello', end='') print('python')
空の行を表示
print('hello') print() print('python')
表示途中で改行する
print('比屋定\n真帆') #\nは改行文字のエスケープシーケンス
キーボードからの読み込み
キーボードから文字列を読み込むにはinput()
関数を使用する
hoge = input() print(hoge)
表示させ、なおかつ、文字列受け取り
hoge = input('入力せい') print(hoge)
複数の文字列をスペースなしで表示するにはsep=''
(+で連結するのでも可能)
hoge = input() print('入力したもじは', hoge, 'です', sep='')
文字列から数値へ
数値へ変換するにはint()
関数を使う
hoge = input() print(int(hoge))
浮動小数点数へ変換するにはfloat()
関数を使う
hoge = input() print(float(hoge))
読み込みと変換をまとめる
hoge = int(input()) print(hoge)
formatメソッド
文字列と整数は連結できないのでformat
メソッドを使用します
文字列.format()
文字列には{}
を書く必要があり、format関数内で{}
に入る値を書きます
hoge = int(input()) print('受け取った数値は{}です'.format(hoge))
定数を表す変数
定数とはその名の通り、定まった数であるため、一箇所変更するだけど、その定数を書いている部分も変更されます。
そして定数を表す変数は大文字と下線記号(_)で表記するのが慣習です。