vuex4 结合 vue3 实现一个状态持久化 vuex 插件

简介:公司项目使用 vue3 已经大半年了,在没有 vue3 生态支持的情况下,把公司项目顺利完成了。期间很多 vue2 时的解决方案没法用呀,没办法只能自己写,期间自己相继写了vue-meta 的简易实现,vue-pdf 的简易实现,vuex-persistedstate 的简易实现,现把vuex-persistedstate的实现记录如下。(vuex-persistedstate 已经出了支持 vue3 的 beta 版本)

本文 github 源码地址

项目依赖:

{
  "core-js": "^3.6.5",
  "vue": "3.0.5",
  "vuex": "^4.0.0"
}

项目目录

.
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
│   ├── favicon.ico
│   └── index.html
└── src
    ├── App.vue
    ├── assets
    │   └── logo.png
    ├── main.js
    └── store
        ├── index.js
        └── plugins
            └── persistedstate.js  # 核心源码实现

核心实现

src/store/plugins/persistedstate.js

export default (options = {}) => {
  const storage = options.storage || (window && window.localStorage)
  const key = options.key || 'vuex'

  // 获取state的值
  const getState = (key, storage) => {
    const value = storage.getItem(key)
    try {
      return typeof value !== 'undefined' ? JSON.parse(value) : undefined
    } catch (err) {
      console.log(err)
    }
    return undefined
  }

  // 设置state的值
  const setState = (key, state, storage) =>
    storage.setItem(key, JSON.stringify(state))

  return store => {
    // 初始化时获取数据,如果有的话,把原来的vuex的state替换掉
    const data = getState(key, storage)
    if (data) {
      store.replaceState(data)
    }

    // 订阅 store 的 mutation。handler 会在每个 mutation 完成后调用,接收 mutation 和经过 mutation 后的状态作为参数
    store.subscribe((mutation, state) => {
      setState(key, state, storage)
    })
  }
}

调用插件

src/store/index.js

import { createStore, createLogger } from 'vuex'
import createPersistedstate from './plugins/persistedstate'
const debug = process.env.NODE_ENV !== 'production'

export default createStore({
  state: {
    loginStatus: 0,
    userInfo: {}
  },
  getters: {
    loginStatus: state => state.loginStatus,
    userInfo: state => state.userInfo
  },
  mutations: {
    SET_USER_INFO(state, userInfo) {
      state.userInfo = userInfo
    },
    SET_LOGIN_STATUS(state, loginStatus) {
      state.loginStatus = loginStatus
    }
  },
  actions: {
    setUserInfo({ commit }, userInfo) {
      commit('SET_USER_INFO', userInfo)
    },
    setLoginStatus({ commit }, loginStatus) {
      commit('SET_LOGIN_STATUS', loginStatus)
    }
  },
  plugins: debug
    ? [createLogger(), createPersistedstate()]
    : [createPersistedstate()]
})

注册 store

src/main.js

import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

createApp(App).use(store).mount('#app')

使用

src/App.vue

<template>
  <img class="avatar" :src="userInfo.avatar" />
  <h1>我的名字叫 {{ userInfo.name }}</h1>

  <button v-if="!loginStatus" @click="handleLogin">登录</button>
  <div v-else>
    <button @click="handleChange">更改身份</button>
    <button @click="handleLogout">退出</button>
  </div>
</template>

<script>
  import { useStore } from 'vuex'
  import { computed } from 'vue'

  export default {
    name: 'App',
    setup() {
      const store = useStore()
      const loginStatus = computed(() => store.getters.loginStatus)
      const userInfo = computed(() => store.getters.userInfo)

      // 登录
      const handleLogin = () => {
        store.dispatch('setLoginStatus', 1)
        store.dispatch('setUserInfo', {
          name: '小明',
          avatar: require('@/assets/logo.png')
        })
      }
      // 更改身份
      const handleChange = () => {
        store.dispatch('setUserInfo', {
          name: Math.random().toString(16).slice(2),
          avatar: require('@/assets/logo.png')
        })
      }
      // 退出
      const handleLogout = () => {
        store.dispatch('setLoginStatus', 0)
        store.dispatch('setUserInfo', {})
      }

      return {
        loginStatus,
        userInfo,
        handleLogout,
        handleLogin,
        handleChange
      }
    }
  }
</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;
  }
  #app .avatar {
    width: 60px;
    border-radius: 50%;
  }
  #app button {
    width: 120px;
    height: 40px;
    line-height: 40px;
    font-size: 16px;
    background-color: #409eff;
    color: #fff;
    border: 0;
    cursor: pointer;
    margin-right: 20px;
  }
  #app button:focus {
    border: 0;
    outline: 0;
  }
</style>

参考链接

1.https://github.com/robinvdvleuten/vuex-persistedstate/blob/master/src/index.ts

2.https://next.vuex.vuejs.org/api/#subscribe

Logo

前往低代码交流专区

更多推荐