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