您现在的位置是:首页 >技术教程 >Vue3中如何使用Vuex---自定义hook网站首页技术教程
Vue3中如何使用Vuex---自定义hook
在Web实际开发中,我们经常会使用 vuex 来管理数据,随着数据量的增加,我们开始使用 vuex 中的语法糖,如 mapState、mapGetters、mapMutations 和 mapActions 等辅助函数来实现快速开发。但是Vue3中使用mapState、mapGetters这些辅助函数时会有些问题产生,本篇文章主要通过对比Vue2中vuex的使用以及分析vuex的源码,最后总结出Vue3中如何使用vuex。
Vue2中vuex及其辅助函数的使用
我们在考虑模块化的情况下,要创建每一个模块的状态数据:
//初始化模块化的vuex ===> store/home/index.js
export default{
namespaced:true,
actions:{},
mutations:{},
getters:{},
state:{name:'csdn',year:2023}
}
然后导入每一个模块的数据到vuex中生成store:
// -- store/index.js --
//引入Vue核心库
import Vue from 'vue
//引入Vuex
import Vuex from 'vuex'
//引入模块化的状态数据
import home from './home'
//使用vuex插件
Vue.use(Vuex)
//创建并暴露store
export default new Vuex.Store({
home, //模块化的vuex状态数据
})
Store成功暴露后,我们需要引入到main.js全局作用域配置。使得在每一个组件实例上都可以使用到$store:
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
Vue.use(VueRouter)
new Vue({
render: h => h(App),
store
}).$mount('#app')
经过上述的步骤,我们就可以在组件中使用vuex管理的数据了:
...
import { mapState } from 'vuex'
...
//这里也可以使用this.$store.name this.$store.year
//但是如果一直这么取vuex中的数据,数据越多就会越麻烦
//所以我们需要使用辅助函数去做数据的提取
computed: {
//数组的写法
...mapsState(['name','year'])
//对象的写法
...mapState({name:"name",year:"year"})
}
Vue3中vuex及其辅助函数的使用
在Vue3中导入每一个模块的数据到vuex中生成store的方式就已经发生了变化,需要使用vuex提供的createStore钩子,其实本质上是vuex进行了升级以更好地迎合Vue3:
// -- store/index.js --
import { createStore } from "vuex";
import home from "./home"
//创建并暴露store
export default createStore({
modules:{
home,
}
})
Store成功暴露后,我们同样需要引入到main.js,然就进行全局注册:
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
//注册createStore创建的store
createApp(App).use(store).mount('#app')
然后在组件中对vuex数据进行管理时,我们使用计算属性时,为了和其他setup中方法保持结构一致,要在setup 中去使用Composition API: computed(()=>{}),其参数需要传入一个函数,我们首先需要知道mapState调用后是否会产生一个函数:
let dataV = mapState(['name','year'])
console.log(dataV)
其输出为:
可以看到传入几个值,就返回几个函数,那我们在computed()组合式API中就无法使用,computed()只能传入一个函数,我们继续看看mapState的源码是如何实现的:
var mapState = normalizeNamespace(function (namespace, states) {
var res = {}; //初始化一个对象,作为返回值的接收
if ((process.env.NODE_ENV !== 'production') && !isValidMap(states)) {
console.error('[vuex] mapState: mapper parameter must be either an Array or an Object');
}
normalizeMap(states).forEach(function (ref) {
var key = ref.key;
var val = ref.val;
//对传入数据进行遍历,返会一个函数
//遍历完成返回的res,会生成多个函数
res[key] = function mappedState () {
//通过 this.$store 来拿到 store 的值的
var state = this.$store.state; //找到store.state
var getters = this.$store.getters;
if (namespace) {
//通过命名空间找到命名空间下的的state
//通过 this.$store 来拿到 store 的值的
var module = getModuleByNamespace(this.$store, 'mapState', namespace);
if (!module) {
return
}
state = module.context.state;
getters = module.context.getters;
}
return typeof val === 'function'
//如果val是函数,调用val,即mapState(state=>state.name)
//不是函数,那就再当前的命名空间下的state中查找state['name]
? val.call(this, state, getters)
: state[val]
};
// mark vuex getter for devtools
res[key].vuex = true;
});
return res
});
我们还发现mapState是通过 this.$store
来拿到 store 的值的,但是在 setup 中是取不到 this 的,setup中的this为undefined。其他的辅助函数(mapGetters、mapMutations、mapActions) 同样都是这样类似的处理。
在我们对源码分析完成后,我们就知道了在 computed 中其实是可以使用mapState的, 是因为 computed 本身就是一个函数,它会接收一个函数作为参数。 我们也知道了辅助函数是被解析成了一个对象,对象中的属性值是函数,调用这个函数就可以得到相应的vuex中的值。我觉得可以自己造一个轮子,把这俩个结合起来去封装一个hooks来使用。
为了满足模块化vuex的需求,我们创建了一个useStore自定义hook,帮助我们在Vue3中使用vuex的辅助函数:
///--- hooks/useStore.js
import { computed } from 'vue'
import { mapGetters, mapState,createNamespacedHelpers,useStore } from 'vuex'
const useFunction = (mapper, mapFn) => {
// params:
// mapper : mapState / mapGetters 中需要传入的参数 对象或者数组
// mapFn : mapState / mapGetters
// store: vue2中的this.$store
let store = useStore()
const storeStateFns = mapFn(mapper)
const storeState = {}
Object.keys(storeStateFns).forEach((keyFn) => {
const fn = storeStateFns[keyFn].bind({ $store: store })
storeState[keyFn] = computed(fn)
})
return storeState
}
export const useState = (moduleName, mapper) => {
let mapperFn = mapState
if (typeof moduleName === 'string' && moduleName.length > 0) {
mapperFn = createNamespacedHelpers(moduleName).mapState
} else {
mapper = moduleName
}
return useFunction (mapper, mapperFn)
}
export const useGetters = (moduleName, mapper) => {
let mapperFn = mapGetters
if (typeof moduleName === 'string' && moduleName.length > 0) {
mapperFn = createNamespacedHelpers(moduleName).mapGetters
} else {
mapper = moduleName
}
return useFunction (mapper, mapperFn)
}
后续使用直接如下即可:
import {useState} from './hooks/useStore'
export default {
setup(){
let storeState = useState('home',['name','year])
return{
...storeState
}
},
}