前言
我们都知道,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')
18 comments
[...]Vue3中全局混入Vuex TypeScript版 (大型项目不建议使用) | 前端打更人 / / body.custom-background { background-color: #f4f5f5; } .post-item-list{ background:#fff; } 首页[...]
src/views/Home.vue:30:22
TS2339: Property 'vuexTestVar' does not exist on type 'ComponentPublicInstance<{}, { handleLogin: () => Promise; }, {}, {}, {}, EmitsOptions, {}, {}, false, ComponentOptionsBase<{}, { handleLogin: () => Promise; }, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, EmitsOptions, string, {}>>'.
28 | },
29 | mounted() {
30 | console.log(this.vuexTestVar)
| ^^^^^^^^^^^
31 | this.$m.vuex('vuexUser.name','123')
32 | console.log(this.vuexUser.name)
33 | },
全局混入了 或者在vuex中声明了这个变量了吗?
控制台能正常打印,只是ts报了这个异常,我这边需要(this as any).vuexTestVar,才不会有这个异常提示
那大概率是declare的问题,我目前的版本没有遇到这个问题过
现在就是有点不太清楚这个声明文件该怎么去改了
再次确认一下你的helloy页面export的是以下内容吗
export default defineComponent({ ... })
vue3里面使用ts 需要定义为defineComponent这个。
是这样的
登录 import { defineComponent, reactive } from 'vue' import { LoginService } from '@/api/common' export default defineComponent({ name: 'Home', setup() { const state = reactive({ Account: 'admin', //账户 Password: 'hhhh', //密码 }) const handleLogin = async () => { const loginParams = { username: state.Account, password: state.Password, } const res = await LoginService.login(loginParams) console.log(res) } return { handleLogin, state } }, data() { return { a: 111 } }, computed: { aa() { return '99999999999999' } }, mounted() { // console.log(vuexTestVar)s console.log(this.state) console.log((this as any).vuexTestVar) // console.log(this.vuexTestVar) console.log(this.aa) this.$m.vuex('vuexUser.name','123') console.log((this as any).vuexUser.name) }, })或者你也可以直接将vuex变量混入到setup中去
在setup不能肯定不能调用混入到 当前页面的data变量呀,你可以写个方法直接从vuex中读取内容
但是我在mounted() {console.log(this.vuexTestVar)} 也是有异常的,没在setup里调用
/ eslint-disable /
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
declare module '*'
有这个声明文件吗
有的
那应该就是declare的问题,没有遇到过
好的,感谢解答,我再自己看一下
来学习了 (❁´◡`❁)
我博客上面TS没高亮
https://learnku.com/articles/53850
可以去这里看看我发在社区里面的
好的୧(๑•̀⌄•́๑)૭