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

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

本文 github 源码地址

项目依赖:

1
{
2
  "core-js": "^3.6.5",
3
  "vue": "3.0.5",
4
  "vuex": "^4.0.0"
5
}

项目目录

1
.
2
├── README.md
3
├── babel.config.js
4
├── package-lock.json
5
├── package.json
6
├── public
7
│   ├── favicon.ico
8
│   └── index.html
9
└── src
10
    ├── App.vue
11
    ├── assets
12
    │   └── logo.png
13
    ├── main.js
14
    └── store
15
        ├── index.js
16
        └── plugins
17
            └── persistedstate.js  # 核心源码实现

核心实现

src/store/plugins/persistedstate.js

1
export default (options = {}) => {
2
  const storage = options.storage || (window && window.localStorage)
3
  const key = options.key || 'vuex'
4
5
  // 获取state的值
6
  const getState = (key, storage) => {
7
    const value = storage.getItem(key)
8
    try {
9
      return typeof value !== 'undefined' ? JSON.parse(value) : undefined
10
    } catch (err) {
11
      console.log(err)
12
    }
13
    return undefined
14
  }
15
16
  // 设置state的值
17
  const setState = (key, state, storage) =>
18
    storage.setItem(key, JSON.stringify(state))
19
20
  return store => {
21
    // 初始化时获取数据,如果有的话,把原来的vuex的state替换掉
22
    const data = getState(key, storage)
23
    if (data) {
24
      store.replaceState(data)
25
    }
26
27
    // 订阅 store 的 mutation。handler 会在每个 mutation 完成后调用,接收 mutation 和经过 mutation 后的状态作为参数
28
    store.subscribe((mutation, state) => {
29
      setState(key, state, storage)
30
    })
31
  }
32
}

调用插件

src/store/index.js

1
import { createStore, createLogger } from 'vuex'
2
import createPersistedstate from './plugins/persistedstate'
3
const debug = process.env.NODE_ENV !== 'production'
4
5
export default createStore({
6
  state: {
7
    loginStatus: 0,
8
    userInfo: {}
9
  },
10
  getters: {
11
    loginStatus: state => state.loginStatus,
12
    userInfo: state => state.userInfo
13
  },
14
  mutations: {
15
    SET_USER_INFO(state, userInfo) {
16
      state.userInfo = userInfo
17
    },
18
    SET_LOGIN_STATUS(state, loginStatus) {
19
      state.loginStatus = loginStatus
20
    }
21
  },
22
  actions: {
23
    setUserInfo({ commit }, userInfo) {
24
      commit('SET_USER_INFO', userInfo)
25
    },
26
    setLoginStatus({ commit }, loginStatus) {
27
      commit('SET_LOGIN_STATUS', loginStatus)
28
    }
29
  },
30
  plugins: debug
31
    ? [createLogger(), createPersistedstate()]
32
    : [createPersistedstate()]
33
})

注册 store

src/main.js

1
import { createApp } from 'vue'
2
import App from './App.vue'
3
import store from './store'
4
5
createApp(App).use(store).mount('#app')

使用

src/App.vue

1
<template>
2
  <img class="avatar" :src="userInfo.avatar" />
3
  <h1>我的名字叫 {{ userInfo.name }}</h1>
4
5
  <button v-if="!loginStatus" @click="handleLogin">登录</button>
6
  <div v-else>
7
    <button @click="handleChange">更改身份</button>
8
    <button @click="handleLogout">退出</button>
9
  </div>
10
</template>
11
12
<script>
13
  import { useStore } from 'vuex'
14
  import { computed } from 'vue'
15
16
  export default {
17
    name: 'App',
18
    setup() {
19
      const store = useStore()
20
      const loginStatus = computed(() => store.getters.loginStatus)
21
      const userInfo = computed(() => store.getters.userInfo)
22
23
      // 登录
24
      const handleLogin = () => {
25
        store.dispatch('setLoginStatus', 1)
26
        store.dispatch('setUserInfo', {
27
          name: '小明',
28
          avatar: require('@/assets/logo.png')
29
        })
30
      }
31
      // 更改身份
32
      const handleChange = () => {
33
        store.dispatch('setUserInfo', {
34
          name: Math.random().toString(16).slice(2),
35
          avatar: require('@/assets/logo.png')
36
        })
37
      }
38
      // 退出
39
      const handleLogout = () => {
40
        store.dispatch('setLoginStatus', 0)
41
        store.dispatch('setUserInfo', {})
42
      }
43
44
      return {
45
        loginStatus,
46
        userInfo,
47
        handleLogout,
48
        handleLogin,
49
        handleChange
50
      }
51
    }
52
  }
53
</script>
54
55
<style>
56
  #app {
57
    font-family: Avenir, Helvetica, Arial, sans-serif;
58
    -webkit-font-smoothing: antialiased;
59
    -moz-osx-font-smoothing: grayscale;
60
    text-align: center;
61
    color: #2c3e50;
62
    margin-top: 60px;
63
  }
64
  #app .avatar {
65
    width: 60px;
66
    border-radius: 50%;
67
  }
68
  #app button {
69
    width: 120px;
70
    height: 40px;
71
    line-height: 40px;
72
    font-size: 16px;
73
    background-color: #409eff;
74
    color: #fff;
75
    border: 0;
76
    cursor: pointer;
77
    margin-right: 20px;
78
  }
79
  #app button:focus {
80
    border: 0;
81
    outline: 0;
82
  }
83
</style>

参考链接

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

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