优雅的在Vue3中使用Vuex「TypeScript版」
前言之前的前言
本菜鸡只是一个会写前端的后端菜鸡,请大佬们轻喷!
但确实,对于 Vue3
的小项目完全可以摒弃 Vuex
来管理项目状态。
前言
我们都知道,vuex 的使用在项目中颇为繁琐,因为它有几大概念使得它不能像普通 ref 或者 data 对象一样直接被我们使用,在我们想要更改 vuex 中的数据时,我们需要通过 mutation
来进行提交,获取 vuex 中存储的变量的时候,我们又需要通过 computed
属性来进行声明,试想,如果项目足够庞大,那么我们使用 vuex 的负担就过于重了,这违背了我们使用状态管理的本意。
问题的提出
首先,我们需要明确,我们想要怎么快捷便利地来使用 vuex ,如果你的想法和我如下所示相同,那么恭喜你,或许本文提出的解决方案适合你。
存储
对于 vuex 的存储我们想要实现的类似如下:
this.$m.vuex(name,value);
我们可以在各个组件或者视图内,通过this来直接使用 vuex 进行存储。
读取
对于 vuex 中数据的读取我们想要实现的类似如下:
<template> <div>{{vuexUser.name}}</div> </template> <script lang="ts"> import {defineComponent} from 'vue' export default defineComponent({ ... methods:{ // 业务方法 xxxxx(){ const flag = this.vuexUser.name? true:false; ..... } } }) </script>
我们能够在模板或者业务逻辑中直接通过 this 直接访问。
解决方案
首先,我们需要在 store 目录下的 index.ts
内加入如下代码
import { createStore } from 'vuex' export default createStore({ state: { vuexIsLogin: false, // 当前的登录状态 vuexTestVar: "这是vuex全局混入的测试变量", vuexUser:{ test: 1, name: "测试名字" } }, mutations: { $changeStore(state: any,payload: any){ // 判断是否为多层级调用,state中为对象存在的情况,诸如user.info.name = 'xxx' const nameArr = payload.name.split('.'); const len = nameArr.length; if (len >= 2){ let obj = state[nameArr[0]]; for (let i = 1 ; i < len - 1 ; i++){ obj = obj[nameArr[i]]; } obj[nameArr[len-1]] = payload.value; }else { state[payload.name] = payload.value; } } } })
之后我们在 store 目录下创建一个 ts 脚本文件,在我的项目中我命名为 maxer.mixin.ts
,在这里,我们就需要使用Vue中的一个特性 Mixin(混入)「不明白的同学可以去官方文档查一查,Vue3的官方中文版已经推出了」
/** * @作者: Seale * @时间: 2021/1/23 * @版本: V1.0 * @说明: maxer Vue 全局混入 * @网站: https://www.imsle.com */ import {mapState} from "vuex"; import store from '@/store/index.ts' import {App} from 'vue' // 将定义的state变量key全部加载到全局变量中 const $mStoreKey = store.state ? Object.keys(store.state) : []; export class Maxer{ vuex = (name: string, value: any): void=>{ store.commit('$changeStore', { name, value }) } } export default<T> (app: App<T>) => { // 进行全局混入 // 将vuex方法挂载到$m中 // 使用方法为:如果要修改vuex的state中的user.name变量为"x" => this.$m.vuex('user.name','x') app.config.globalProperties.$m = new Maxer(); app.mixin({ computed: { // 将vuex的state中的所有变量,解构到全局混入的mixin中 ...mapState($mStoreKey) } }) }
在这里我们使用全局混入,将 vuex 的 state 混入到计算属性中,我们就可以通过类似 this.vuexName
来进行调用,还有一点建议就是,由于采用全局混入的模式将 vuex 中的数据进行混入,所以我们应该用特定的字符来进行标识 vuex 中的数据,我的建议是在 vuex 中变量前加上 vuex_
prefix ,或者类似 vuexVar:'xxx'
来进行声明变量。
之后我们就需要在 main.ts 中进行初始化了。
import { createApp } from 'vue' import App from './App.vue' import store from './store' import installMaxerStore, {Maxer} from './store/maxer.mixin' ... // 声明全局组件 防止需要this调用时不能识别类型 declare module '@vue/runtime-core' { interface ComponentCustomProperties { $m: Maxer; // 声明全局方法 } } ... const app = createApp(App) installMaxerStore(app) // 全局混入vuex app.use(store).mount('#app')
之后我们就可以愉快地使用 vuex 了(在注意变量命名的情况下)。
Vuex 数据持久化
这个时候或许你会发现,当页面进行刷新的时候,vuex 的数据会进行初始化(回到最初的状态),这个是由于 vuex 是运行在内存中的,同样的,它的数据也是存储在内存中,当用户进行刷新页面的操作,所以内存数据会重新进行初始化。
那么我们就可以通过 sessionStorage / localStorage / cookie 来进行数据的持久化存储。
页面加载的时候先读取域中的缓存数据,如果有则覆写。当页面将要刷新前,我们将 vuex 的数据存储到域中。
这里建议使用 sessionStorage ,对于需要长时间持久化的数据再使用 localStorage 或者 cookie。
同样我们在 store 目录下新建一个 ts 脚本。
/** * @作者: Seale * @时间: 2021/1/24 * @版本: V1.0 * @说明: vuex数据持久化,防止F5之后数据消失 * @网站: https://www.imsle.com */ import {Store} from "vuex"; export default<T> (store: Store<T>): void=>{ // 不需要持久化的数据存入sessionStorage if (sessionStorage.getItem('store')){ store.replaceState( Object.assign( {}, store.state, JSON.parse(sessionStorage.getItem('store') as string) ) ); // 移除sessionStorage中的数据 sessionStorage.removeItem("store"); } // 页面刷新的时候进行持久化 window.addEventListener('beforeunload',()=>{ sessionStorage.setItem("store", JSON.stringify(store.state)); }) }
对于需要长时间持久化的数据建议自行定义规则进行封装。
之后我们需要在入口文件中进行声明
import { createApp } from 'vue' import App from './App.vue' import store from './store' import installMaxerStore, {Maxer} from './store/maxer.mixin' import initStorePersistence from './store/store.persistence' ... // 声明全局组件 declare module '@vue/runtime-core' { interface ComponentCustomProperties { $m: Maxer; // 声明全局方法 } } ... const app = createApp(App) installMaxerStore(app) // 全局混入vuex initStorePersistence(store) // 初始化持久化vuex app.use(store).mount('#app')#前端##学习路径#