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


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