# 前端废物的自救之路(3)使用Cookie完成Nuxt.js中Vuex的持久化

# 为什么需要Vuex持久化

在开发的过程中,我们会使用Vuex来存储用户信息、Token等内容,但Vuex的信息是存储在内存中的,当页面刷新时,内容自然就丢失了。我们需要寻找其他的办法使得刷新页面时Vuex的信息能保存下来,也就是Vuex的持久化。

# Nuxt.js中的Vuex持久化方案及其可用性

使用Vue.js时,我们一般用vuex-persistvuex-persistedstate等框架完成Vuex的持久化,也并不会出什么问题。但是在Nuxt中,情况就不一样了。

在搜索引擎上搜索Nuxt Vuex持久化,可以找到以下解决方案:

  1. vuex-persistedstate配合js-cookie
    • 地址:nuxt中vuex数据持久化

    • 可用性:不可用,按照文中的方法配置仍然出现找不到window对象的情况。在配置插件的时候配置了ssr: false,仍然找不到window对象,推测可能是NuxtVuex的版本问题(注:这篇文章是19年12月的)。

  2. vuex-persist
  3. nuxtServerInit方法配合cookie-universal-nuxt

# 终极解决方案:js-cookie配合nuxtServerInit实现Vuex持久化

常见的解决方案都不可用,那么我们就需要终极解决方案:js-cookie配合nuxtServerInit

实现起来也是很简单的。

npm	install --save js-cookie

# 第二步:设置Vuex时同时设置Cookie

以登录为例,在拿到用户信息后使用js-cookie把用户信息设置到Cookie中。注意Cookie的过期时间要和服务端设置的Token的过期时间保持一致(服务端设置Token的过期时间是4小时),否则Token过期后再访问需要认证的页面会出现循环重定向的问题(我配置了根据Cookie判断用户是否登录的逻辑,并且登录后再访问登录页面会被重定向到后台管理,所以才会出现这样的问题,当然如果没有做这样的配置的话就无所谓了,或者你也可以配置在Token失效后清除Cookie的操作)。

注意expires属性的单位是天,如果需要设置为小时的话可以传入小数。

@/pages/login.vue

import { mapMutations } from 'vuex'
import * as Cookies from 'js-cookie'

export default {
  // ...
  methods: {
    ...mapMutations(['setUserInfo']),
    login () {
      this.$refs.loginForm.validate((valid) => {
        if (valid) {
          // 表单校验通过,开始登录
          this.$axios.post('/login', {
            username: this.form.username,
            password: this.form.password
          }).then((res) => {
            // 登录成功,向Vuex中设置用户信息
            this.setUserInfo({
              token: res.token,
              userId: res.id,
              username: res.username
            })
            // Cookie的过期时间与Token的过期时间一致,为4小时
            Cookies.set('token', res.token, { expires: 1 / 6 })
            Cookies.set('userId', res.user_id, { expires: 1 / 6 })
            Cookies.set('username', res.username, { expires: 1 / 6 })
            this.$router.push('/admin')
          }).catch((err) => {
            this.msg = err.response.data.message
          })
        } else {
          return false
        }
      })
    },
    // ...
  }
}

# 第三步:设置Vuex

我们刚刚设置了保存Cookie的逻辑,接下来就需要把Cookie的信息取出来,设置在Vuex中,完成持久化操作。

nuxtServerInit方法在每次发送请求且请求未到达页面的时候都会被调用,可以借助这个方法来设置Vuex。

@/store/index.js

export const state = () => ({
  token: '',
  userId: 0,
  username: ''
})

export const mutations = {
  // 用户登录时,需要设置用户信息
  setUserInfo (state, loginInfo) {
    state.token = loginInfo.token
    state.userId = loginInfo.userId
    state.username = loginInfo.username
  },
  // 用户登出时,需要删除用户信息
  removeUserInfo (state) {
    state.token = ''
    state.username = ''
    state.userId = 0
  }
}

export const actions = {
  nuxtServerInit ({ commit, store }, { req }) {
    // 切分Cookie
    const cookie = req.headers.cookie.split(';')
    // 定义字符常量:需要从cookie中取出的值的名称
    const tk = 'token='
    const un = 'username='
    const uid = 'userId='

    // 需要持久化的值
    let token = ''
    let username = ''
    let userId = 0

    // 遍历Cookie,取得需要的值
    cookie.forEach((e) => {
      if (e.includes(tk)) {
        token = e.split(tk)[1]
      } else if (e.includes(un)) {
        username = e.split(un)[1]
      } else if (e.includes(uid)) {
        userId = e.split(uid)[1]
      }
    })

    // 提交mutation
    commit('setUserInfo', {
      token,
      username,
      userId
    })
  }
}

注意这里我为了解决使用传统写法时控制台报Warn的问题而采用了Nuxt建议的写法。

设置完成并重启应用之后,再次刷新页面就不会出现vuex数据被清空而需要重新登录的问题了。