スクラッチから始めるNuxt.jsでログイン!

開発Divのeciです。

今年はVue Fes Japanが秋にあることですし、周りがみんな「vue,vue」言い始めたので、Vue.jsを触ってみました。

調べていくとNuxt.jsというフレームワークがよさげですので、今回はこれのチュートリアルを元に拡張していこうと思います。
Nuxt.jsにはスターターテンプレートが用意されていますが、今回は「スクラッチから始める」の方を参考にしていきます。

インストール - Nuxt.js

ただ、このままですと味気ないですので、ログイン認証まで作っていきます。

では、行きましょう!

まずは、Helloページ表示まで

ディレクトリを作成

$ mkdir hello
$ cd hello

package.jsonの作成

helloディレクトリ直下にpackage.jsonを作ります。

{
  "name": "hello",
  "scripts": {
    "dev": "nuxt"
  }
}

Nuxt.jsのインストール

npm install --save nuxt

最初のページを作成

$ mkdir pages
$ cd pages

pagesディレクトリに、index.vueを作ります。

<template>
  <div class="container">
    <h1>hello</h1>
  </div>
</template>

起動

$ npm run dev

ブラウザで「localhost:3000」に行くと、「hello」と表示されましたか?

ログイン認証の実装

ここからいよいよログイン認証を作って行きます。
ちなみに、ここを参考にしています。

やること

  1. ログインページの作成
  2. ストアの用意
  3. ミドルウェアの実装
  4. nuxt.config.jsの用意
  5. hello.vueの作成(indexページだけだと味気ないので...)
  6. index.vueの修正

ログインページの作成

pagesディレクトリにlogin.vueを作ります。

<template>
  <div class="container">
    <form @submit.prevent="login">
      <p class="error" v-if="formError">{{ formError }}</p>
      <p>name:<input type="text" v-model="formUsername" name="username" /></p>
      <p>password:<input type="text" v-model="formPassword" name="password" /></p>
      <button type="submit">ログイン</button>
    </form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      formError: null,
      formUsername: "demo",
      formPassword: "pass",
    }
  },
  methods: {
    async login() {
      try {
        await this.$store.dispatch('login', {
            username: this.formUsername,
            password: this.formPassword
        })
        this.$router.push('/')
      } catch(e) {
        this.formError = e.message
      }
    },
  }
}
</script>

ストアの用意

Vueでグローバルなステートを管理するために、Vuexというライブラリが用意されています。

$ mkdir store
$ cd store

storeディレクトリにindex.jsを作成します。

export const state = () => ({
  authUser: null,
})

export const mutations = {
  SET_USER: function (state, data) {
    if (data) {
      state.authUser = data
    } else {
      state.authUser = null 
    }
  }
}

export const actions = {
  async login({ commit }, { username, password }) {
    try {
      //const res = await axios.post('/login', { username, password })  本当は、たぶんこんな感じ
      if (username != "demo" || password != "pass") {
        throw new Error("エラーですよ")
      }
      commit('SET_USER', username)
    } catch (error) {
      throw error
    }
  },
  async logout({ commit }) {
    try {
      commit('SET_USER', null)
    } catch(error) {
      throw error
    }
  }
}

ミドルウェアの設定

Nuxt.jsでは、あるページがレンダリングされる前に実行される関数を、middlewareという仕組みを使って実装できますので、 これを使って各ページの認証を行います。

$ mkdir middleware
$ cd middleware

middlewareディレクトリにauth.jsを作ります。

middleware/auth.js

export default function ({ route, store, redirect }) {
    if (!store.state.authUser && route.path != "/login") {
      return redirect('/login')
    }
  }

ストアのauthUserが空だった場合は、「/login」に遷移します。

nuxt.config.jsの用意

helloディレクトリ直下にnuxt.config.jsを用意します。

module.exports = {
  head: {
    title: 'Hello',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', content: 'Hello' }
    ]
  },
  //mode: 'spa',
  router: {
    middleware: 'auth'
  },
}

hello.vueの作成

pagesディレクトリにhello.vueを作成します。

<template>
  <div class="container">
    <h1>こんにちは</h1>
    <nuxt-link to="/">Topページへ</nuxt-link>
  </div>
</template>

index.vueの修正

helloページへのリンクおよび、ログアウトボタンを追加します。

pages/index.vue

<template>
  <div class="container">
    <h1>スクラッチから始めるNuxtログイン</h1>
    <button v-on:click="logout">ログアウト</button>
    <nuxt-link to="/hello">helloページへ</nuxt-link>
  </div>
</template>

<script>
export default {
  data() {
    return {
      formError: null,
      formUsername: "demo",
      formPassword: "pass",
    }
  },
  methods: {
    async logout() {
      try {
        this.$store.dispatch('logout').then(() => {
          this.$router.push('/login')
        })
      } catch (e) {
        this.formError = e.message
      }
    }
  }
}
</script>

起動および動作確認

$ npm run dev

ログアウトした状態で「/hello」にアクセスしても、無事「/login」に戻ってきますね。

まとめ

htmlとjs合わせて100行ちょっとでログイン認証の雛形が出来てしまいました。
また、学習コストもかなり低いと思いました。
まだ始めたばかりですので、よりよい書き方を追求していきたいと思います。