Nuxt.js_Vuexの永続化

Nuxt Vuexの永続化

Vuexとは

Vuexとは、Vueアプリケーションで使用する状態管理ライブラリです。

コンポーネント間でのデータ受け渡しを効率化する(アプリケーション上のどこからでもアクセスできる)ための仕組みとなります。


ただし、Vuex にアプリケーションの状態を保存しても、リロードしたタイミングでデータはリセットされてしまいます。

APIトークンなどの永続保持したい用途には対応できません。


vuex-persistedstateについて

vuex-persistedstate は Vuex Store のデータを localstorage に自動的に格納させるためのライブラリです。

vuex-persistedstateを使うとVuexのステート情報はブラウザのlocalStorageを使って永続化されます。

この仕組みにより、ブラウザのリロード時もVuexのステート情報を保持することができます。


vuex-persistedstateのインストール

Nuxtプロジェクトフォルダで、下記コマンドを実行します。


$ npm install vuex-persistedstate --save

APIトークンの保持

REST APIで認証トークンを取得し、vueページで任意に利用する例です。

ここでは、AcccessToken、IDToken、RefreshTokenの3トークンを扱います。


Storeファイルを作成する。

store/index.jsを用意します。

assets/config.jsonにURLやclientIDなどを記載しています。


import createPersistedState from 'vuex-persistedstate'
import config from '~/assets/config.json'
const axios = require('axios')

export const state = () => ({
  idToken: null,
  accessToken: null,
  refreshToken: null,
  loginDate: null
})

export const mutations = {
  setLogin(state, info) {
    state.idToken = info.idToken
    state.accessToken = info.accessToken
    state.refreshToken = info.refreshToken
    state.loginDate = new Date()
  },
  setLogout(state) {
    state.idToken = null
    state.accessToken = null
    state.refreshToken = null
    state.loginDate = null
  },
  setRefreshToken(state, info) {
    state.idToken = info.idToken
    state.accessToken = info.accessToken
    state.loginDate = new Date()
  }
}

const localMethods = {
  isTokenExpired(preLoginDate) {
    const preDate = new Date(preLoginDate)
    const nowDate = new Date()

    // 一時間を超えているか判定する。
    if ((nowDate.getTime() - preDate.getTime()) > (60 * 60 * 1000)) {
      return true
    }
    return false
  }
}

export const actions = {
  isLoggedIn(context) {
    try {
      if ((!context.state.idToken) || (!context.state.accessToken)) {
        return false
      }
      if (!context.state.refreshToken) {
        return false
      }

      if (localMethods.isTokenExpired(context.state.loginDate)) {
        // The token is already expired.
        return false
      }
      return true
    } catch (error) {
      return false
    }
  },

  async login(context, { username, password }) {
    try {
      const url = config.ApiUrl + '/login'
      const headers = { 'Content-Type': 'application/json;charset=UTF-8' }
      const data = {
        'username': username,
        'password': password,
        'poolId': config.poolId,
        'clientId': config.clientId
      }
      const res = await axios.post(url, data, { headers: headers })
      context.commit('setLogin', res.data)
    } catch (error) {
      throw error
    }
  },

  async logout(context) {
    try {
      if (localMethods.isTokenExpired(context.state.loginDate)) {
        // The token is already expired.
        return
      }

      const url = config.ApiUrl + '/logout'
      const axiosInstance = axios.create({
        headers: {
          'Authorization': context.state.accessToken
        }
      })
      await axiosInstance.post(url)
      context.commit('setLogout')
    } catch (error) {
      throw error
    }
  },

  async refreshToken(context) {
    try {
      if (localMethods.isTokenExpired(context.state.loginDate) === false) {
        return
      }

      const url = config.ApiUrl + '/refresh'
      const headers = { 'Content-Type': 'application/json;charset=UTF-8' }
      const data = {
        'clientId': config.clientId,
        'refreshToken': context.state.refreshToken
      }

      const res = await axios.post(url, data, { headers: headers })
      context.commit('setRefreshToken', res.data)
    } catch (error) {
      throw error
    }
  }
}

export const plugins = [
  createPersistedState()
]

Storeを参照する。

pages/login.vueからログインを実行してトークンをstoreに保存します。


<template>
 ...(略)
</template>
<script>
export default {
  data: () => ({
    username: '',
    password: ''
  }),
  methods: {
    execLoginAPI: async function () {
      try {
        await this.$store.dispatch('login', {
          username: this.username,
          password: this.password
        })
        return true
      } catch (e) {
        return false
      }
    }
  }
}
</script>

ログアウトする場合は下記のように定義します。


  logout: async function () {
    try {
      await this.$store.dispatch('logout')
    } catch (e) {
      console.log(e)
    }
    this.$router.replace('/')
  }

トークンを用いて他のAPIを実行する場合には下記のように定義します。


  execUpdateAPI: async function () {
    try {
      await this.$store.dispatch('refreshToken')

      const url = config.ApiUrl + '/data/' + String(this.item.id)
      const headers = {
        'content-type': 'application/json',
        'Authorization': this.$store.state.idToken
      }
      const putBody = {
        'accessToken': this.$store.state.accessToken,
        'data': this.updatedata,
      }
      await axios.put(url, putBody, { headers: headers })
      return true
    } catch (error) {
      this.setErrorMessage(error)
      return false
    }
  }

関連ページ