Nuxt.jsで簡単なログイン機能をつくってみる

nuxt.jsでアプリを開発する案件があり、勉強することになったのですが、結局はnode.jsのexpressで開発することになったので、nuxtの知識を応用することがあまりできませんでした。 でもせっかく勉強したのでここら辺で一旦、気楽にアウトプットしておきたいと思います。 このエントリーはそんなノリで書かれています。

ログイン機能の実装

nuxtの使い方とかは省きます。 nodeの入れ方とか、vue-cliとかで調べるとすぐできます。

まずログインする画面です。

// pages/signin.vue

<template>
  <div class="login-form">
    <form @submit.prevent="login">
      <p class="error" v-if="error">{{ error }}</p>
      <p><input type="text" v-model="email" placeholder="email" name="email"/></p>
      <p><input type="text" v-model="password" placeholder="password" name="password"/></p>
      <div class="login-btn">
        <button type="submit">ログイン</button>
      </div>
    </form>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        error: null,
        email: "",
        password: "",
      }
    },
    methods: {
      async login() {
        try {
          await this.$store.dispatch("login", {
            email: this.email,
            password: this.password
          })
          this.$router.push("/")
        } catch(e) {
          this.error = e.message
        }
      }
    }
  }
</script>

一般的なログインフォームをhtmlで作成し、 script内で定義したデータにフォーム内で入力された内容が格納されます。

v-modelで指定しているのは、scriptでexport defaultをしている中の、 data(){return: ~}の中の要素です。

また、htmlのformタグに@submit.prevent="login"と設定しているので、 script内のmethodsに定義したメソッドlogin()がそのフォームの送信時(ログインのボタンを押した時)に起動します。

login()メソッドの中身の仕組みは以下の通り。 1. formに入力されたdataの要素をstoreへ送ります。 (this.$store.dispatchの部分) 2. awaitをしているので、1.が完了してから次の処理("/"のページへリダイレクト)へ移ります。エラーならcatch(e)の方の処理(エラーメッセージの表示)を行います。

要素の送り先であるstoreの構造は以下の画像を参考にしてみてください。

vuex.png

流れとしては、 1. 緑のVue Componentsというのが先に載せたsignin.vueの中身となります。 2. そこからdispatchして、storeにあるactions.js(にあるloginメソッド)に要素を渡します。 3. その後commitを行なってmutations.jsに同じ要素、または整形した要素を渡し、 4. 最終的にその内容がstateに反映されるようになります(mutate)。 5. 最後のrenderでstateをhtmlに適用することができるようになります。

// store/actions.js
export default {
  async login({ commit }, { email, password }) {
    console.log(commit)
    try {
      // ここでは仮に
      // メールアドレス: test@email
      // パスワード: 123456789
      // が登録されてある状態を想定しています。
      if (email != "test@email" || password != 123456789) {
        throw new Error("error!!!")
      }
      // 入力したメールアドレスとパスワードが
      // すでに登録されているメールアドレスとパスワードと一致した場合、変数dataに入力値が渡されます。
      let data = { email: email, password: password }
      // 変数dataのを次のmutations.jsにあるAUTHED_USERメソッドに渡します。
      commit("AUTHED_USER", data)
    } catch (e) {
      throw e
    }
  }
}
// store/mutations.js

export default {
  AUTHED_USER: function (state, data) {
    state.authUser = data // 入力したemailとpasswordがここに入る
  }
}

このactions.jsから渡ってきた値が格納される、state.authUserというのは、 store/index.jsのstateに定義されたauthUserを表しています。 この時のstore/index.jsは以下のようになります。

// store/index.js

import Vuex from "vuex"
import mutations from "./mutations"
import actions from "./actions"

const store = () => {
  return new Vuex.Store({
    state: {
      // ユーザーのログイン状況フラグ
      authUser: null
    },
      mutations,
      actions
    })
}
export default store

これでログイン機能はひとまず落ち着きました。 ですが、そもそも登録をしないでログインができるサービスなんてものはありませんから、今度は新規登録ページを実装していきましょう。

新規登録機能の実装

// pages/signup.vue

<template>
  <div class="signup-form">
    <form @submit.prevent="registration">
      <p class="error" v-if="error">{{ error }}</p>
      <p><input type="text" v-model="email" placeholder="email"/></p>
      <p><input type="text" v-model="password" placeholder="password"/></p>
      <div class="signup-btn">
        <button type="submit">Sign up</button>
      </div>
    </form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      error: null,
      email: "",
      password: ""
    }
  },
  methods: {
    async registration() {
      try {
        await this.$store.dispatch("registration", {
          email: this.email,
          password: this.password
        })
        this.$router.push("/")
      } catch(e) {
        this.formError = e.message
      }
    }
  }
}
</script>

vueファイルは簡単にフォームを設け、 そこで入力された値がactions.jsに送られることになります。

# store/actions.js

async registration({ commit }, { email, password }) {
  try {
    // サインアップ時のvalidationチェック
    // @がなければ無効且つパスワードは5字以上10字以下の数字を必要とする
    if (email.includes("@") === false) {
      throw new Error("メールアドレスには@が含まれている必要があります。")
    } else if (password.length >= 10 || password.length <= 5) {
      throw new Error("パスワードは5文字以上10文字以下で作成してください。")
    }
    let data = { email: email, password: password }
    commit("REGISTER_USER", data)
  } catch(e) {
    throw e
  }
},
// 上で作成したlogin処理
async login({ commit }, { email, password }) {
  console.log(commit)
  try {
    if (email != "test@email" || password != 123456789) {
      throw new Error("error!!!")
    }
    let data = { email: email, password: password }
    // 変数dataのを次のmutations.jsにあるAUTHED_USERメソッドに渡します。
    commit("AUTHED_USER", data)
  } catch (e) {
    throw e
  }
}

registration(新規登録)では、mutations.jsのREGISTER_USERへ整形した値(ここではメールアドレスとパスワードのバリデーション処理)を渡しています。

// store/mutations.js

export default {
  REGISTER_USER: function (state, data) {
    state.registeredUser["email"] = data["email"]
    state.registeredUser["password"] = data["password"]
    state.authUser = data
  },
  AUTHED_USER: function (state, data) {
    state.authUser = data // 入力したemailとpasswordがここに入る
  }
}

mutations.jsのREGISTER_USERでstore/index.jsのstateに定義されたregistered_user(emailとpasswordのプロパティを持っている)に、 新登録フォーム入力された値が格納されるようになります。 故にstore/index.jsは以下のようになります。

// store/index.js

import Vuex from "vuex"
import mutations from "./mutations"
import actions from "./actions"

const store = () => {
  return new Vuex.Store({
    state: {
      registered_user: {},
      authUser: null
    },
      mutations,
      actions
    })
}
export default store

そして、このstore/index.jsでregistered_userを定義したあと、 signin.vueのscriptを以下のように変更します。

// pages/signin.vue
<script>
  export default {
    data() {
      return {
        error: null,
        email: "",
        password: "",
      }
    },
    methods: {
      async login() {
        console.log(this.registeredUser)
        try {
          await this.$store.dispatch("login", {
            email: this.email,
            password: this.password,
            // ここに登録した情報と、ログイン時に入力した情報を照らし合わせるために、登録された値を置きます。
            authData: this.$store.state.registeredUser
          })
          this.$router.push("/")
        } catch(e) {
          this.error = e.message
        }
      }
    }
  }
</script>

または、

// pages/signin.vue

import { mapState } from "Vuex"

export default {
  (省略)......
  computed: mapState([
    "registeredUser"
  ])
  (省略)......
}

とすることで this.$store.state.registeredUser を this.registeredUser に置き換えることができます。

そして、store/actions.jsで登録時の情報とログイン時に入力した情報を照らし合わせる処理を書きます。

// store/actions.js

async registration({ commit }, { email, password }) {
  try {
    if (email.includes("@") === false) {
      throw new Error("メールアドレスには@が含まれている必要があります。")
    } else if (password.length >= 10 || password.length <= 5) {
      throw new Error("パスワードは5文字以上10文字以下で作成してください。")
    }
    let data = { email: email, password: password }
    commit("REGISTER_USER", data)
  } catch(e) {
    throw e
  }
},
async login({ commit }, { email, password, authUser }) {
  console.log(commit)
  try {
    // authUserには渡された登録ユーザーのメールアドレスとパスワードが格納されています。
    // それらの値と入力された値が異なればエラー、同じなら次の処理に進みます。
    if (email != authUser.email || password != authUser.password) {
      throw new Error("error!!!")
    }
    let data = { email: email, password: password }
    commit("AUTHED_USER", data)
  } catch (e) {
    throw e
  }
}

これで新規登録ができるようになり、その登録情報を基にログインすることができる仕組みを作ることができました!

あとはログアウトについてですが、これはstoreのauthUserにnullを渡してあげれば良いだけなので、 actions.jsにログアウトボタンを押した時に発火する処理を作成して、mutations.jsのAUTHED_USERにnullを渡してあげれば問題ないです。

まあでも、もっとちゃんとしたログイン機能を作るんならまだまだ色んなことしないといけないのでしょうけど、、、、、、

気楽といったものの、割と長くなりました。 おつかれさまです。