Help us understand the problem. What is going on with this article?

Nuxt.jsで味わうFirebaseハンズオン

More than 1 year has passed since last update.

0. 下準備

$ npm install -g firebase-tools
  • npxのインストール(global)
$ npm install -g npx

1. Nuxt.jsのインストールと起動確認

  • Nuxt.jsのインストール
npx create-nuxt-app firenuxt
# 設定内容
# > Generating Nuxt.js project in /Users/keisuke/workspace/nuxtjs/firenuxt
# ? Project name firenuxt
# ? Project description My marvelous Nuxt.js project
# ? Use a custom server framework none
# ? Use a custom UI framework none
# ? Choose rendering mode Universal
# ? Use axios module yes
# ? Use eslint no
# ? Use prettier no
# ? Author name keisuke shingaki
# ? Choose a package manager npm

  • 開発サーバーの起動
$ cd firenuxt
$ npm run dev
# http://localhost:3000で表示が確認できる

スクリーンショット 2018-12-03 16.32.43.png


1. Firebaseのセットアップ

$ firebase init
# 設定内容
# ? Which Firebase CLI features do you want to setup for this folder? Press Space to select features, then Enter to confirm your choices. Hosting: Configure and deploy Firebase Hosting sites
# ? Select a default Firebase project for this directory: firenuxt-30550 (firenuxt)
# ? What do you want to use as your public directory? public
# ? Configure as a single-page app (rewrite all urls to /index.html)? No
  • 下記のような構成になる
.
├── .editorconfig
├── .firebaserc # firebaseのプロジェクトファイル
├── .git
├── .gitignore
├── .nuxt
├── README.md
├── assets
├── components
├── firebase.json # firebaseの設定ファイル
├── layouts
├── middleware
├── node_modules
├── nuxt.config.js
├── package-lock.json
├── package.json
├── pages
├── plugins
├── public
├── static
└── store

2. Nuxt.jsのビルドとFirebase Hostingへのデプロイ

  • Nuxt.jsのビルド
$ npm run generate
  • firebaseのpublicルートの変更(firebase.json)
firebase.json
{
  "hosting": {
-   "public": "public",
+   "public": "dist",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  }
}


  • デプロイ
$ firebase deploy

スクリーンショット 2018-12-03 15.27.58.png


2. Firebase Authenticationで認証

2.1 Loginテンプレートを作成

pages/index.vue
<template>
  <section class="container">
    <div>

      <!-- 新規登録フォーム -->
      <section>
        <h5>新規登録</h5>
        <p><input type="text" placeholder="メールアドレス"></p>
        <p><input type="password" placeholder="パスワード"></p>
        <div class="links">
          <button>新規登録</button>
        </div>
      </section>

      <!-- ログインフォーム -->
      <section>
        <h5>ログイン</h5>
        <p><input type="text" placeholder="メールアドレス"></p>
        <p><input type="password" placeholder="パスワード"></p>
        <div class="links">
          <button>ログイン</button>
        </div>
      </section>

      <!-- マイページ -->
      <section>
        <h5>ログイン中です</h5>
        <!-- ログイン中ユーザーのメールアドレスを表示 -->
        <p>メールアドレス:</p>
        <div class="links">
          <button>ログアウト</button>
        </div>
        <div>
          <ul><li>めっせーじ</li></ul>
        </div>
        <div class="links">
          <p><input type="text" placeholder="メッセージ"></p>
          <button>メッセージ追加</button>
        </div>
      </section>

    </div>
  </section>
</template>

<style>
.container {
  padding: 100px;
}
</style>


2.2 Firebase Authenticationの設定

  • ライブラリのインストール
$ npm install firebase
plugins/firebase.js
import firebase from 'firebase'

if (!firebase.apps.length) {
    firebase.initializeApp({
        apiKey: "xxxxx",
        authDomain: "xxxxx",
        databaseURL: "xxxxx",
        projectId: "xxxxx",
        storageBucket: "xxxxx",
        messagingSenderId: "xxxxx"
    })
}

export default firebase

  • FirebaseコンソールでAuthenticatioでメール/パスワードの認証を有効にする スクリーンショット 2018-12-03 17.55.57.png

2.3 Firebase Authenticationで新規登録

  • APIアクセス部分を作成
api/UserAPI.js
import firebase from '@/plugins/firebase'

export default class UserAPI {

    /**
     * メールアドレスとパスワードで新規ユーザー登録を実施します。
     * @param {string} mailAddress 
     * @param {string} password 
     */
    static regist(mailAddress, password) {
        return firebase.auth().createUserWithEmailAndPassword(mailAddress, password);
    }

    /**
     * メールアドレスとパスワードでログインを実施します。
     * @param {string} mailAddress 
     * @param {string} password 
     */
    static login(mailAddress, password) {
        return firebase.auth().signInWithEmailAndPassword(mailAddress, password);
    }

    /**
     * ログアウトします。
     */
    static logout() {
        return firebase.auth().signOut();
    }

    /**
     * ユーザー情報を取得します。
     */
    static getUser() {
        return firebase.auth().currentUser;
    }
}

  • Store部分を作成
store/user.js
// UserAPIのインポート
import UserAPI from "@/api/UserAPI"

export const state = () => ({
    isLogin: false,
    emailAddress: ""
})

export const mutations = {
    // ユーザー情報を設定します。
    setEntity(state, user) {
        state.emailAddress = user.email;
    },
    // stateを初期化します。
    clear(state) {
        state.emailAddress = "";
        state.isLogin = false;
    },
    // ログイン状態を設定します。
    setIsLogin(state, isLogin) {
        state.isLogin = isLogin;
    }
}

export const actions = {
    // ユーザー情報を取得します。
    load: ({ commit }) => {
        var currentUser = UserAPI.getUser();
        if (currentUser) {
            // ログイン済み
            commit('setIsLogin', true);
            commit('setEntity', currentUser);
        } else {
            // 未ログイン
            commit('setIsLogin', false);
            commit('clear');
        }
    },
    // 新規ユーザー登録を行います。
    regist: ({ commit, dispatch }, payload) => {
        return UserAPI.regist(payload.mailAddress, payload.password)
            .then((res) => {
                commit('setIsLogin', true);
                dispatch('setEntity', res.user);
            })
            .catch(function(error) {
                commit('setIsLogin', false);
                commit('clear');
            });
    },
    // ユーザーログインを行います。
    login: ({ commit, dispatch }, payload) => {
        return UserAPI.login(payload.mailAddress, payload.password)
            .then((res) => {
                commit('setIsLogin', true);
                dispatch('setEntity', res.user);
            })
            .catch(function(error) {
                commit('setIsLogin', false);
                commit('clear');
            });
    },
    // ログアウトを行います。
    logout: ({ commit }) => {
        return UserAPI.logout()
            .then((res) => {
                commit('setIsLogin', false);
                commit('clear');
            })
            .catch(function(error) {
                console.log(error);
            });
    },
    // 新規ユーザー登録を行います。
    setEntity: ({ commit }, user) => {
        commit('setEntity', user);
    }
}

  • viewファイルへ組み込み
pages/index.vue
<template>
  <section class="container">
    <div>

      <!-- 新規登録フォーム -->
      <section v-if="!isLogin">
        <h5>新規登録</h5>
        <p><input type="text" v-model="mailAddress" placeholder="メールアドレス"></p>
        <p><input type="password" v-model="password" placeholder="パスワード"></p>
        <div class="links">
          <button @click="regist()">新規登録</button>
        </div>
      </section>

      <!-- ログインフォーム -->
      <section v-if="!isLogin">
        <h5>ログイン</h5>
        <p><input type="text" v-model="mailAddress" placeholder="メールアドレス"></p>
        <p><input type="password" v-model="password" placeholder="パスワード"></p>
        <div class="links">
          <button @click="login()">ログイン</button>
        </div>
      </section>

      <!-- マイページ -->
      <section v-if="isLogin">
        <h5>ログイン中です</h5>
        <!-- ログイン中ユーザーのメールアドレスを表示 -->
        <p>メールアドレス:{{$store.state.user.emailAddress}}</p>
        <div class="links">
          <button @click="logout()">ログアウト</button>
        </div>
      </section>

    </div>
  </section>
</template>

<script>
export default {
  data: function(){
    return {
        mailAddress: '',
        password: '',
        content: '',
    }
  },
  computed: {
    isLogin(){
        return this.$store.state.user.isLogin;
    }
  },
  methods: {
    init: function() {
        this.password = "";
        this.mailAddress = "";
        this.content = "";
    },
    regist: function () {
        console.log("regist")
        this.$store.dispatch('user/regist', {mailAddress:this.mailAddress, password:this.password});
        this.init();
    },
    login: function () {
        this.$store.dispatch('user/login', {mailAddress:this.mailAddress, password:this.password});
        this.init();
        this.getMessages()
    },
    logout : function() {
        this.$store.dispatch('user/logout');
    }
  },
}
</script>

<style>
.container {
  padding: 100px;
}
</style>


3. Firebase Firestoreでデータを保存

3.1 Firestoreの設定

firenuxt_–_Database_–_Firebase_console.jpg

3.2 設定ファイルの編集

plugins/firebase.js
import firebase from 'firebase'

if (!firebase.apps.length) {
    firebase.initializeApp({
        apiKey: "xxxxx",
        authDomain: "xxxxx",
        databaseURL: "xxxxx",
        projectId: "xxxxx",
        storageBucket: "xxxxx",
        messagingSenderId: "xxxxx"
    })
}

// DB設定を追加
const firestore = firebase.firestore();
const settings = { timestampsInSnapshots: true };
firestore.settings(settings);

export default firebase

  • API部分の実装
api/MessageAPI.js
import firebase from '@/plugins/firebase'

export default class MessageAPI {

    /**
     * メッセージのリストを取得
     */
    static getMessages() {
        const messagesRef = firebase.firestore().collection('messages')
        return messagesRef.orderBy('created', 'desc').get()
    }

    /**
     * メッセージを追加
     * @param {string} message
     */
    static addMessage(messageContent) {
        let message = {
            content: messageContent,
            created: Date.now()
        }
        return firebase.firestore().collection('messages').add(message)
    }
}

  • Store部分を作成
store/message.js
// MessageAPIのインポート
import MessageAPI from "@/api/MessageAPI"

export const state = () => ({
    items: []
})

export const mutations = {
    setEntity(state, items) {
        state.items = items;
    },
    clear(state) {
        state.items = [];
    }
}

export const actions = {
    load: ({ commit }) => {
        return MessageAPI.getMessages()
            .then((res) => {
                let messages = []
                res.forEach(doc => {
                    let data = {
                        id: doc.id,
                        content: doc.data().content
                    }
                    messages.push(data)
                })
                commit('setEntity', messages)
            })
            .catch(function(error) {
                console.log(error)
                commit('clear');
            });
    },
    add: ({ commit, dispatch }, payload) => {
        return MessageAPI.addMessage(payload.messageContent)
            .then((res) => {
                dispatch('load');
            })
            .catch(function(error) {
                commit('clear');
            });
    },
}
  • viewファイルへ組み込み
pages/index.vue
<template>
  <section class="container">
    <div>

      <!-- 新規登録フォーム -->
      <section v-if="!isLogin">
        <h5>新規登録</h5>
        <p><input type="text" v-model="mailAddress" placeholder="メールアドレス"></p>
        <p><input type="password" v-model="password" placeholder="パスワード"></p>
        <div class="links">
          <button @click="regist()">新規登録</button>
        </div>
      </section>

      <!-- ログインフォーム -->
      <section v-if="!isLogin">
        <h5>ログイン</h5>
        <p><input type="text" v-model="mailAddress" placeholder="メールアドレス"></p>
        <p><input type="password" v-model="password" placeholder="パスワード"></p>
        <div class="links">
          <button @click="login()">ログイン</button>
        </div>
      </section>

      <!-- マイページ -->
      <section v-if="isLogin">
        <h5>ログイン中です</h5>
        <!-- ログイン中ユーザーのメールアドレスを表示 -->
        <p>メールアドレス:{{$store.state.user.emailAddress}}</p>
        <div class="links">
          <button @click="logout()">ログアウト</button>
        </div>
        <div>
          <ul v-for="(item,index) in $store.state.message.items" :key="index">
       <li>{{ item.content }}</li>
          </ul>
        </div>
        <div class="links">
          <p><input type="text" v-model="content" placeholder="メッセージ"></p>
          <button @click="addMessage()">メッセージ追加</button>
        </div>
      </section>

    </div>
  </section>
</template>

<script>
export default {
  data: function(){
    return {
        mailAddress: '',
        password: '',
        content: '',
    }
  },
  computed: {
    isLogin(){
        return this.$store.state.user.isLogin;
    }
  },
  methods: {
    init: function() {
        this.password = "";
        this.mailAddress = "";
        this.content = "";
    },
    regist: function () {
        console.log("regist")
        this.$store.dispatch('user/regist', {mailAddress:this.mailAddress, password:this.password});
        this.init();
    },
    login: function () {
        this.$store.dispatch('user/login', {mailAddress:this.mailAddress, password:this.password});
        this.init();
        this.getMessages()
    },
    getMessages: function() {
      this.$store.dispatch('message/load');
    },
    addMessage: function() {
      this.$store.dispatch('message/add', { messageContent: this.content });
      this.init();
    },
    logout : function() {
        this.$store.dispatch('user/logout');
    }
  },
}
</script>

<style>
.container {
  padding: 100px;
}
</style>


  • 実装画面

スクリーンショット 2018-12-19 18.51.53.png

スクリーンショット 2018-12-19 18.52.12.png

参考
Nuxt.jsとFirebaseでWebアプリケーション開発 - Speaker Deck
https://speakerdeck.com/takec24/nuxt-dot-jstofirebasedewebapurikesiyonkai-fa?slide=20

Firebaseの各機能を3行で説明する - Qiita
https://qiita.com/shibukk/items/4a015c5b3296563ac19d

Nuxt v2とFirebase(CloudFirestore)でPWA対応Webアプリ開発 - Qiita
https://qiita.com/_takeshi_24/items/3ee051e1db1b3e8da106

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした