您现在的位置是:首页 >技术教程 >Vue3中如何使用Vuex---自定义hook网站首页技术教程

Vue3中如何使用Vuex---自定义hook

杜同学。 2023-06-13 00:00:03
简介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
      }
  },
}

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。