Nuxt.js+Firebase Cloud Messagingを使ったPWA化が簡単で衝撃的だった

2019/10/12

2018/03/13投稿ではてなブログから移行した内容です.

概要

Nuxt.jsで作ったシステムをPWA化させた.

PWA化を実装してみて,PWAの素晴らしさ・Nuxt.jsの素晴らしさが更にわかったので伝えたい!
そして,Nuxt.js+Firebase Clouds Messaging(以下,FCM)に関する記事が少なかったため,実装方法を書いていく.

背景

上記で説明したVIROというシステムは,友達に既に使って貰っている.

そこで出てきた文句として,

  • 毎回ログインすることが面倒
  • ブラウザの上下が邪魔(URLを入力する部分,戻るボタンや他のタブを表示するボタン)
  • プッシュ通知が欲しい などなど...

スマホアプリのような機能が欲しいというのが基本的だった.
どう対応するか悩んでいたところ,小学校の同級生がPWAについて推していたことを思い出した.
調べてみると簡単に実装できそうだったので,PWA化した.

PWAとは?

Progressive Web Appsの略称で,WebサイトでスマホアプリのようなUXを実現させるというもの.

Web Applicationのその先という感じですかね.
PWAの代表的な例としては,

  • オフライン対応
  • ホーム画面に追加
  • プッシュ通知

などがある.

PWAの利点は?

自分が実装してみての利点は,WebAppを作成するだけで,ある程度アプリ層までも作れるということ.

Web技術でネイティブアプリのようなものを作る技術は前からあったと思う. (Monacaなど)
でも,学習コスト・実装コストは高く,SwiftやKotlinを勉強した方がよくねって思ってしまう.

しかし,PWA化は学習コスト・実装コストが低いためおすすめである.
そして,使ってもらう側の一番の利点は,ダウンロード不要ということが一番だと思う.

※PWAの利点や詳しい内容は以下のサイト見てください. いまさら聞けないPWAとAMP

Nuxt.jsとは?

Vue.jsの環境をいい感じにやってくれて,SSRが簡単にできる.
この記事を見ている方は,Nuxt.jsに詳しい方ばかりだと思うので省略.

オフライン対応・ホーム画面に追加

Nuxt.jsの場合,

  • PWAモジュールの追加
  • 画像の追加
  • 設定ファイルの修正

を行うだけで,PWA化ができる(あら簡単)

Nuxt PWA

詳しい実装方法は,素晴らしい記事がたくさんあるので,以下のサイトを見てください.

Nuxt.jsで始めるPWA

PWAをNuxt.jsで簡単に体験する

DBに関しては,Firebase Realtime Databaseを利用していたため,既にオフライン対応している.

プッシュ通知対応

プッシュ通知に関しては,OneSignalを使うのが一般的みたい.

今回は,Firebase Realtime Databaseを利用していることやOneSignalは色々と面倒だったので,Firebase Cloud Messagingを利用することにした.

FCMは,
設定ファイルを書き,
10行ほど書けば,プッシュ通知を受け取る状態にできる.

後は,サーバ側からでもフロント側からでも内容を送信すれば通知が表示されるようになる.
Firebaseの公式から,実装方法の動画があるため,お兄さんの言う通りに書いていけば実装できる.

メッセージを受信するようにブラウザを設定する

manifest.jsonにハードコード値を指定するのだが,Nuxt.jsではnuxt.config.jsに記述する.

manifest: {
    "gcm_sender_id": "103953800507"
}

この値は固定なので,どんな人でもこの値になる.
上記の記事で,既にPWAを実装している場合,nameやtitle,lang,theme_colorなどのプロパティが記述されているはず.

Service Workerの設定

Nuxt.jsの場合,staticディレクトリにfirebase-messaging-sw.jsを作成する.

FCMでは,Service Workerのファイル名が決まっているみたい.(Service Workerをちゃんと理解できてない)
まず始めに,コンソールから送信者IDを取得する.
コンソール→プロジェクトの設定→クラウドメッセージングタブ

20180313023029

20180313023032

firebase-messaging-sw.js

importScripts('https://www.gstatic.com/firebasejs/4.0.0/firebase-app.js')
importScripts('https://www.gstatic.com/firebasejs/4.0.0/firebase-messaging.js')

const config = {
    apiKey: "",
    messagingSenderId: "コンソールから取得した送信者ID"
}

if (!firebase.apps.length) {
    const firebaseApp = firebase.initializeApp(config)
}

const messaging = firebase.messaging()

プッシュ通知の受信を実装

今回は,トップ画面で利用しているコンポーネントやpagesで実装した.
僕はcreated()内に実装.

const messaging = firebase.messaging()
messaging.requestPermission()
    .then(() => {
        console.log('Have permission')
        return messaging.getToken() //ユーザにプッシュ通知を表示する権限の許可を表示
    ]).then((currentToken) => {
        if (currentToken) {
            // プッシュ通知を受信し,表示できる状態
        }
    }).catch((err) => {
        console.log('Error Occurred.')
    })

これで実装完了.
後は通知を送るのみ.

プッシュ通知の送信

今回は,Firebaseのみで完結させたかったため,フロント側でデータベースのお知らせ情報が追加された場合,
自分に向けてプッシュ通知を送るという実装にした.

import firebase from 'firebase'

export default {
    created () {
        firebase.database().ref('notifications/' + uid).on('value', (snapshot) => { //この部分はFirebase Realtime Database部分
                const messaging = firebase.messaging()
                messaging.requestPermission()
                    .then(() => {
                        console.log('Have permission')
                        return messaging.getToken() //ユーザにプッシュ通知を表示する権限の許可を表示
                    }).then((currentToken) => {
                        if (currentToken) {
                            // この部分までは上記で実装しました.
                            // この部分はデータベースのお知らせ情報が追加などの修正がおきると呼ばれる
                            // 今回はこの部分に送信部分を記述していく
                        }
                    }).catch((err) => {
                        console.log('Error Occurred.')
                    })
        }
    }
}

プッシュ通知を送る場合, プッシュ通知を送るためのURLに

  • 受信者のトークンID
  • 通知内容
  • 送信者のサーバーキー(送信者IDの場所と同じ.コンソール→プロジェクトの設定→クラウドメッセージングタブ)

をPOST送信することで送信される.

if (currentToken) {
    let argObj = { // 受信者のトークンIDと通知内容
        to: currentToken,
        notification: {
            body: 'メッセージ内容'
            title: 'タイトル',
            click_action: 'クリックした際に開くURL',
            icon: 'アイコン'
        }
    }
    let optionObj = { //送信者のサーバーキー
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'サーバーキー'
        }
    }
    axis.post('https://fcm.googleapis.com/fcm/send', argObj, optionObj)
}

これで送信部分も実装できたと思う.