LoginSignup
5
1

More than 3 years have passed since last update.

初心者でもできるVueとFirebaseでSPAなスマホでできる子ども用足し算練習webサービスをリリースする方法

Last updated at Posted at 2019-08-15

完成形

準備

vue cliを使う。
https://cli.vuejs.org/
ここでつまづいたら、インストール方法に関する記事はこの辺
https://qiita.com/kamitomo/items/34451b11caaf51bd2498

$ vue --version
3.10.0

(コマンド実行する所と結果を見分けるため、コマンド実行している所は先頭に$を付ける)

準備は以上。

vueに必要なファイルを自動生成してもらう

$ vue create ui

uiはフォルダ名。好きに書き換えて良い。

下記のように質問来るけど、defaultでenterを押す

Vue CLI v3.10.0
? Please pick a preset: (Use arrow keys)
❯ default (babel, eslint) 
  Manually select features 

これでvueに必要なファイルの作成は完了。

下記のようなコマンド実行すると、localhostでサーバーが起動する。

 $ cd ui
 $ yarn serve
$ yarn serve
 DONE  Compiled successfully in 4303ms                                                                                                                                     9:59:38 AM

  App running at:
  - Local:   http://localhost:8082/ 

  Note that the development build is not optimized.
  To create a production build, run yarn build.

ポート番号は空いている所が自動的に。
(ちなみに、vue cli 3.10.0からは、--portがサポートされたようで)

ブラウザで見ると、こんな感じに

トップページを書き換える

トップページはsrc/App.vueというファイルになる。

書き換え前はこんな感じ

src/App.vue
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'app',
  components: {
    HelloWorld
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

書き換え後

src/App.vue
<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: 'app',
}
</script>

<style>
</style>

1行<router-view></router-view>を追加したので注意。
ここにページが自動的に埋まるようになる。
それ以外の所は装飾して遊んでみてください。

vue routerを使ってみる

これを使うと複数のページをURLに従って切り替えしてくれる。
先ほどの<router-view></router-view>が切り替わる。
今回の範囲は1ページなので使わなくていいけど、拡張できるように使ってみる。

先程作成したuiフォルダで

$ yarn add vue-router

これで、vue routerが使えるようになったので、vue routerを使うjsファイルを作成する。
ファイル名は何でも良い。

src/router.js
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router);

function loadView(view) {
    return () => import(`@/views/${view}.vue`)
}

const router = new Router({
    mode: 'history',
    routes: [
        {
            path: "/",
            name: "check",
            component: loadView('Check'),
        },
    ]
})

export default router

ブラウザで/のパスの場合に表示するページがCheck.vueになるという設定で。
だいたいこんな感じで動く。

router.jsを使う設定

main.jsが本当に中心となるファイルで、ここに書くと実行してもらえるようにできている。
App.vueもここから呼び出されている。
ここでrouterを読み込んで、vueに使ってもらうように設定する。
router.jsを読み込むコードはimport router from './router'

src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

./routerのように.jsは省略(書いてもいいけど)
importで'vue'みたいに先頭に./書いていないやつはライブラリ。
ライブラリは先ほどのようにyarn addみたいな感じでもらってくる。
importで@/って書いてあったらはsrc/の事。今回は入ってない。
という知識を持っていたら色々なサンプルコードが読めるようになる。

Check.vueの作成

src/views/Check.vueというファイルを作成する

src/views/Check.vue
<template>
    <div>check</div>
</template>

<script>
    export default {
        name: "Check"
    }
</script>

<style scoped>

</style>

これは、先程のrouter.jsから呼び出すファイルとなる。

ここまでの実装が完了すると、ブラウザで開いた場合は、こんな感じ

あとは、ここの実装を増やしていくだけで終わり。

v-if, data, methodsを使ってみる

計算中か計算中以外の表示を切り替える。
ボタンを押したら切り替わるようになる。

Check.vue
<template>
    <div>
        <div v-if="isChecking">
            <button @click="checkAns">こたえあわせ</button>
        </div>
        <div v-else>
            <button @click="start">スタート</button>
        </div>
    </div>
</template>

<script>
    export default {
        name: "Check",
        data: () => ({
            isChecking: false,
        }),
        methods: {
            checkAns() {
                this.isChecking = false
            },
            start() {
                this.isChecking = true
            }
        },
    }
</script>

<style scoped>
    button {
        font-size: 24px;
    }
    input {
        font-size: 24px;
        width: 60px;
    }
    div {
        font-size: 24px;
    }
</style>

画面切り替えはこれで完成。
あとはロジックを増やしていくだけ。
styleは好みで設定。
タグの中に<div style="font-size: 24px">でもよい。

{{{xxxx}}}とv-modelを使ってみる

  • 計算できるように、ランダムの数字を表示する
  • 入力できるようにする

Check.vue
...変更なし
        <div v-if="isChecking">
            <br>
            {{leftNum}} + {{rightNum}} =
            <input type="number" v-model="ans" style="font-size: 24px; width: 60px">
            <br>
            <button @click="checkAns" style="font-size: 24px">こたえあわせ</button>
        </div>
...変更なし
<script>
    export default {
        name: "Check",
        data: () => ({
            leftNum: 0,
            rightNum: 0,
            ans: "",
            isChecking: false,
        }),
        methods: {
            checkAns() {
                this.isChecking = false
            },
            next() {
                const l = Math.floor(Math.random() * 9) + 1
                const r = Math.floor(Math.random() * 9) + 1
                this.leftNum = l
                this.rightNum = r
                this.ans = ""
            },
            start() {
                this.isChecking = true
                this.next()
            }
        },
    }
</script>
  • {{{変数名}}}で、dataの値が表示できる
  • v-model="変数名"で、inputのデータをセットすることができる

ほぼ出来上がった感がある。

watchを使ってみる

  • 計算にかかった時間を計測する。
  • isCheckingがtrue中の時間を計測すればよい
  • 単にwatchを使いたいだけで、実際は使わなくてもいいけど・・・

Check.js

        <div v-else>
            <br>
            <button @click="start" style="font-size: 24px">スタート</button>
            <br>
            <div>
                じかん:{{elapsedTime}}びょう
            </div>
        </div>
...       
        data: () => ({
            leftNum: 0,
            rightNum: 0,
            ans: "",
            isChecking: false,
            startTime: 0,
            elapsedTime: 0,
        }),
        methods:{
         ....
        },
        watch: {
            isChecking(v) { // isCheckingが変更されるたびに呼ばれる
                if (v) { // isCheckingがtrueに変更
                    this.startTime = Date.now()
                } else { // isCheckingがfalseに変更
                    this.elapsedTime = Math.floor((Date.now() - this.startTime) / 1000)
                }
            }
        },

UXを向上させる

このままだと入力が難しい!!!

  • スマホブラウザで電卓キーボードに切り替える
  • オートフォーカス(Vue.nextTickを使ってみる)

電卓キーボードに

スマホでみた時に、入力しやすさを考えて電卓キーボードに変更したい

Check.js
  <input type="number" v-model="ans" pattern="\d*" id="ans">

オートフォーカス(Vue.nextTickを使ってみる)

  • こたえあわせを押す毎にキーボードが閉じられるので、入力枠にオートフォーカスする必要がある
  • オートフォーカスは画面の描写が終わった後にしかできない難しさがあるので、技を使う必要がある
...
<script>
    import Vue from 'vue'

            next() {
                const l = Math.floor(Math.random() * 9) + 1
                const r = Math.floor(Math.random() * 9) + 1
                this.leftNum = l
                this.rightNum = r
                this.ans = ""
                Vue.nextTick(function () {
                    document.getElementById('ans').focus();
                })
            },

この書き方なら応用がきくと思うので、ここではこんな感じで書いてみた。

リリース用のファイルを作成する

$ yarn build

これでdistフォルダが自動的に作成され、その中のファイルが公開用のファイル

Webサービスとして全世界に公開

Firebase Hostingの設定

  • firebaseのhostingを使うと先程作ったSPAを簡単に公開できる
  • https://firebase.google.com/?hl=ja
  • firebaseにアクセスしてプロジェクトを追加をクリック

プロジェクト名は好きな名前で(ここで付けた名前がURLになる)

「今は必要ない」を選択して「プロジェクトを作成」をクリックする。
A/Bテスト・・・とかなんとか色々できそうな雰囲気が伝わってくるが、webサービスでは使えないものが並んでいる。

ここでHostingをクリックする

ここで始めるをクリック

表示される順番でコマンドを実行する。
注意点があるとすると、firebase loginログイン済みみたいな感じになったら、firebase logoutを実行して、再度実行すると、後に問題が出ない。

一番最初に作ったuiフォルダ内でfirebase initを実行する
上下キーでHostingを選択して、スペースキーで選択して、enter

$ cd ui
$ firebase init
....
 ◯ Database: Deploy Firebase Realtime Database Rules
 ◯ Firestore: Deploy rules and create indexes for Firestore
 ◯ Functions: Configure and deploy Cloud Functions
❯◯ Hosting: Configure and deploy Firebase Hosting sites
 ◯ Storage: Deploy Cloud Storage security rules

先程作成したプロジェクト名を選択してenter

? Select a default Firebase project for this directory: 
  [don't setup a default project] 
❯ test97668183 (test97668183) 
  [create a new project] 

先程yarn build作成したdist と入力してenterすると、distフォルダがアップロードできる

? What do you want to use as your public directory? (public) dist

親切に、シングルページアプリケーションか?と聞いてくるのでyを入力

? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) y

index.htmlがあるけど上書きするか?と聞いてくるので、N

? File dist/index.html already exists. Overwrite? (y/N) 

distフォルダをサーバー(firebase hosting)にアップロード

$ firebase deploy

完成。

URLはfirebaseapp.comだけではない

デプロイ後にURLはfirebaseapp.comのほうを案内されるけど、web.appでもアクセスできる。

https://edu-keisan.firebaseapp.com/
https://edu-keisan.web.app/

アクセス先は同じ。

補足:xxxx.mapは削除してみたい

yarn build の時に使われる設定例としてやってみる。

ui/vue.config.js
module.exports = {
    productionSourceMap: false,
}

このファイルを作成して、yarn buildすると、mapファイルが作成されていない。
あとは、これをfirebase deployとするとアップロードされる。

補足:ルート以外のパスで動かしたい

URLのパスでルート以外で動作させたいという気になった場合は、publicPathを設定する。

ui/vue.config.js
module.exports = {
    publicPath: 'calc',
    productionSourceMap: false,
}

とかとか、あとは好きに検索して遊んでみて。

最後に

せいかい、まちがいカウントに関しては、ここまで来ると自力で作れると思うので、あとは自力でやってみてください。
ここにあるコードでは出てきません。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1