Visual Studio Code + Vue.js(Nuxt.js) + Firebase(RealtimeDatabase)で簡単なTODOアプリを作る

# 概要

公式やいろんな方の Qiita の記事を参考にしながら下記を行ったのでメモしておきます。 
メモなので解説なしの箇所もあります。 
また、勉強中のため間違っていることもあると思います。その際は指摘いただけるとありがたいです。

- Vue.js(Nuxt.js)や Firebase まわりの開発環境構築
- Visual Studio Code (以降 VSCode)の開発環境構築
- Vue.js(Nuxt.js) + Vuex(Vuexfire) + Firebase(Database)を使って簡易な TODO アプリを作る
- Netlify で公開

[作ったもの](https://github.com/h-orito/sandbox-nuxt)

## 環境

- Mac
- Node.js 9.4.0

# Vue.js や Firebase まわり

さくさく入れましょう。

## vue-cli

```bash
$ npm install -g vue-cli
```

## ESLint

最低限の環境としては必要ないかもしれませんがあると便利系も入れていきます。 
ESLint は JavaScript の静的検証ツールです。
こちらの記事を参考にさせていただきました。 
[ESLint 最初の一歩](https://qiita.com/mysticatea/items/f523dab04a25f617c87d)

```bash
$ npm install -g eslint
$ eslint -v
v5.6.0
```

## Firebase

```bash
$ npm install firebase
```

## VuexFire

Vue.js の状態管理を行う Vuex と Firebase をつなぐためのライブラリです。

```bash
$ npm install vuexfire
```

## dotenv

[dotenv-module](https://github.com/nuxt-community/dotenv-module) 
Nuxt.js のプロジェクトルートに`.env`ファイルを置いておくと環境変数として読み取ってくれるやつです。 
Firebase の key だとかは環境変数に設定したいと思ったのですが、 
production 環境では環境変数に設定するとして、ローカル開発の際に環境変数をいちいち設定したり汚したくない...と思ったので導入。

```bash
$ npm install @nuxtjs/dotenv
```

プロジェクト内での設定は後述します。

# VSCode の環境構築

こちらの記事を参考に導入してください。(投げやり) 
[Visual Studio Code で Vue.js アプリケーションの開発環境を構築する](https://qiita.com/rubytomato@github/items/b35b819671e7cbb3dff7)

追加した拡張機能

- npm
- Vetur
- ESLint
- Prettier

 この後 Nuxt.js プロジェクト作成後にワークスペース設定も仕込みます。

# Todo アプリを作る

## プロジェクト作成

Nuxt.js(Vue.js 製フロントエンドフレームワーク)で作ります。詳しくは公式サイトをみてください。 
[Nuxt.js のインストールガイド](https://ja.nuxtjs.org/guide/installation) を参考にしました

```bash
$ vue init nuxt-community/starter-template sandbox-nuxt

? Project name sandbox-nuxt
? Project description Nuxt.js project
? Author your-name <your mail address>
```

作成できたら、VSCode で作成した`sandbox-nuxt`を開きます。

## 動作確認

```bash
# 依存ライブラリのインストール
$ npm install

# 起動
$ npm run dev
```

`http://localhost:3000/`で起動したよ、とメッセージが出るので開けるか確認。

## VSCode の設定続き

設定 → ワークスペースの設定 → setting.json を開く
でワークスペースの設定画面を開いて下記に書き換え

```json
{
  "files.eol": "\n",
  "prettier.eslintIntegration": true,
  "prettier.semi": false,
  "prettier.singleQuote": true,
  "vetur.format.defaultFormatter.js": "vscode-typescript",
  "javascript.format.insertSpaceBeforeFunctionParenthesis": true,
  "typescript.format.insertSpaceBeforeFunctionParenthesis": true,
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    {
      "language": "vue",
      "autoFix": true
    }
  ],
  "eslint.autoFixOnSave": true
}
```

基本的に先ほどの 
[Visual Studio Code で Vue.js アプリケーションの開発環境を構築する](https://qiita.com/rubytomato@github/items/b35b819671e7cbb3dff7) 
のまま導入してます。後で変えたくなったら調べながら変えていけば良いので。

# ここで Netlify に公開してみる

まだプロジェクトができたばかりで TODO アプリはできていませんが、 
ここでホスティングサービスに公開してみます。 
無料の静的ホスティングサービスはいくつかありますが、今回は[Netlify](https://www.netlify.com/)にします。

## GitHub/GitLab/Bitbucket に push

先ほど作ったプロジェクトを上記のどこかに push しておいてください。 
Netlify ではこのうちどれかのアカウントと連携してログインできます。

## Netlify で公開

こちらの記事の通りに進めると公開できます。（投げやり） 
いやほんとこれ以上書くことがないです。 
ドメイン設定以降は今回はやっていません。
[vue-cli で web アプリケーションを作って、Netlify を使って無料で爆速でリリースした話](https://qiita.com/tiwu_official/items/5d1e883b3190cd8de56f)

あっさり公開できました。すごい時代です。
デフォルト設定だと`master`ブランチに push されるたびに再デプロイしてくれるので、一旦このままでいきます。

# 開発に戻る前に、Firebase の RealtimeDatabase を設定しておく

最終的に Firebase のリアルタイム DB に TODO 内容を保存するようにしたいので、設定しておきます。 
https://firebase.google.com/?hl=ja 
特に迷うポイントもないので文章のみで文章のみで細かいところは省略します。 
（解説サイトも多いので困っても調べられると思います）

1. 無料プランで始める
2. プロジェクト作成
3. Database → Realtime Database と進む
4. 「todos」を作っておく

どのタイミングで聞かれるか忘れましたが、DB の read/write 権限は特に制限なしにしました。 
ちゃんとしたアプリを作るときは認証等を絡めて設計したほうが良いです。

# TODO アプリを作る

こちらの記事を参考にしました。 
[Vue と Nuxt と Firebase でサクッとアプリを作る](https://qiita.com/wiro34/items/d29694b4649ebdb875ce) 
[Vue.js+Vuex+VuexFire で TodoList](https://qiita.com/frost_star/items/eea52cb33a749272d948)

TODO を追加/削除できる簡単なページを作ります。 
リアルタイム DB を使い、他の人が追加/削除したら自分の画面にも反映されるようにします。

## Vuex と VuexFire の設定

状態管理と Firebase との繋ぎこみのところです。  
Nuxt.js がいい感じにやってくれるので、Nuxt.js と VuexFire のドキュメントに従うだけです。
VuexFire は Firebase → Vuex へのバインディングをしてくれますが、 
逆方向はやってくれないので、更新は Firebase の API を叩く必要があります。 
そのため追加削除の関数も用意しています。

```javascript:store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import firebase from '../plugins/firebase'
import { firebaseMutations, firebaseAction } from 'vuexfire'
import { ADD_TODO, REMOVE_TODO, INIT_TODO } from './action-types'

const db = firebase.database()
const todosRef = db.ref('/todos')

export default () =>
  new Vuex.Store({
    state: {
      todos: []
    },
    mutations: {
      ...firebaseMutations
    },
    actions: {
      [INIT_TODO]: firebaseAction(({ bindFirebaseRef }) => {
        bindFirebaseRef('todos', todosRef, { wait: true })
      }),
      [ADD_TODO]: firebaseAction((context, text) => {
        todosRef.push(text)
      }),
      [REMOVE_TODO]: firebaseAction((context, key) => {
        todosRef.child(key).remove()
      })
    },
    getters: {
      getTodos: state => state.todos
    }
  })
```

```javascript:store/action-types.js
export const ADD_TODO = 'ADD_TODO'
export const REMOVE_TODO = 'REMOVE_TODO'
export const INIT_TODO = 'INIT_TODO'
```

```javascript:plugins/firebase.js
import firebase from 'firebase'

// 環境変数から取得
const config = {
  databaseURL: process.env.FIREBASE_DATABASEURL,
  apiKey: process.env.FIREBASE_API_KEY,
  authDomain: process.env.FIREBASE_AUTH_DOMAIN,
  projectId: process.env.FIREBASE_PROJECTID,
  storageBucket: process.env.FIREBASE_STORAGEBUCKET,
  messagingSenderId: process.env.FIREBASE_MESSAGINGSENDERID
}

if (firebase.apps.length === 0) {
  firebase.initializeApp(config)
}

export default firebase
```

## 画面

```vuejs:pages/index.vue
<template lang="html">
    <div id="todo">
      <input type="text" @keyup.enter="addTodo" v-model="todoText"/>
      <li v-for="todo in todos" :key="todo['.key']">
        {{todo['.value']}}
        <button @click="removeTodo(todo['.key'])">X</button>
      </li>
    </div>
</template>

<script>
import { ADD_TODO, REMOVE_TODO, INIT_TODO } from '../store/action-types';
export default {
    data () {
        return {
            todoText: '',
        };
    },
    computed: {
        todos () {
            return this.$store.getters.getTodos;
        },
    },
    methods: {
        addTodo () {
            this.$store.dispatch(ADD_TODO, this.todoText);
            this.todoText = '';
        },
        removeTodo (key) {
            this.$store.dispatch(REMOVE_TODO, key);
        },
    },
    created () {
        this.$store.dispatch(INIT_TODO)
    },
};
</script>

<style lang="css">
</style>
```

## dotenv で環境変数を.env から取得

Firebase の  設定の箇所で環境変数を使っていますが、 
ローカル開発でいちいち環境変数に設定したくないので、 
`.env`ファイルに設定して読み込ませます。
（production 環境では別途環境変数に設定するので、コミットされないように.gitignore にも追加しておきましょう）

このあたりの設定値は Firebase の「設定」→「ウェブアプリに Firebase を追加」あたりで取得できます。

```bash:.env
FIREBASE_API_KEY=******
FIREBASE_AUTH_DOMAIN=******
FIREBASE_DATABASEURL=******
FIREBASE_PROJECTID=******
FIREBASE_STORAGEBUCKET=******
FIREBASE_MESSAGINGSENDERID=******
```

また、`nuxt.config.js`に設定を追加して`dotenv`を使って`.env`ファイルを読み込ませるようにします。

```javascript:nuxt.config.js
module.exports = {
  // 他の設定はそのままで下記追加

  env: {
    FIREBASE_API_KEY: process.env.FIREBASE_API_KEY,
    FIREBASE_AUTH_DOMAIN: process.env.FIREBASE_AUTH_DOMAIN,
    FIREBASE_DATABASEURL: process.env.FIREBASE_DATABASEURL,
    FIREBASE_PROJECTID: process.env.FIREBASE_PROJECTID,
    FIREBASE_STORAGEBUCKET: process.env.FIREBASE_STORAGEBUCKET,
    FIREBASE_MESSAGINGSENDERID: process.env.FIREBASE_MESSAGINGSENDERID
  },

  modules: ['@nuxtjs/dotenv']
}
```

これでローカル環境では`.env`の設定値が使われるようになります。 
（変数名が小文字だとうまく読み込めなかったので注意。結構ハマりました...）

## Netlify 側で環境変数設定

Deploy Settings → Build environment variables 
に先ほど`.env`に設定した内容を設定しておくだけです。らくちん。

# 完成

- 画面を開いたらFirebaseのDBに保存されている内容が出る
- 追加/削除した内容がFirebaseのDBに保存される
- Firebase側で変更しても画面側に即反映される
- 2画面開いて片方で変更したらもう片方にも即反映される

あっさりここまでできました。
もちろんpushすれば先ほど公開したNetlify側のサイトにも反映されます。

公式も見て回りましたが、色んな記事を参考にさせていただいて効率的に進めることができました。
ありがとうございました。