vue3使用Vuex4状态管理

385 阅读2分钟

温馨提示版本信息: "vue": "^3.0.0", "vuex": "^4.0.2"

vuex原理传送门

vuex中存储着一些共用的数据对象,方便所有的组件进行使用;

vue3中的vuex也是用了compositionAPI

store/index.js

import { createStore } from "vuex"
const store = createStore({
  state() {return {}},
  getters: {},
  mutations: {},
  actions: {},
  modules: {}
})
​
​
export default store

state——数据

state就是用于存储vuex数据的地方,由于是共用的,一处发生更改,每个地方都发生更改。

state() {
    return {
      counter: 0,
      name: "coder",
      age: 18,
      books: [
        {name: "VueJs", price: 100, count: 10},
        {name: "React", price: 150, count: 26},
        {name: "JavaScript", price:199, count: 18},
        {name: "NodeJS", price: 240, count: 70},
      ],
      discount: 0.6
    }
  },

将state的值一般都是放入在计算属性中

import { mapState, useStore } from "vuex"
import { computed } from "vue"

optionsAPI:

    computed: {
​
      // 从state里面取对应的值,放入计算属性汇总
      // 1. 数组形式
      // ...mapState(["name", "counter", "age"])
​
      // 2. 对象形式
      ...mapState({
        sCounter: state => state.counter,
        sName: state => state.name
      })
    },

compositionAPI:

    setup() {
      const store = useStore()
​
      // const sCounter = computed(() => store.state.counter)
      // mapState 返回的每一项都是函数
      const storeState = useState(["name", "age"])
      const storeState2 = useState({
        sCounter: state => state.counter
      })
      // const storeState = mapState({
      //   sCounter: () => {}
      // })
      // 绝活
      // Object.keys(storeState).forEach( fnKey => {
      //   const fn = storeState[fnKey].bind({$store: store})
      //   storeState[fnKey] = computed(fn)
      // })
      return {
        ...storeState,
        ...storeState2
      }
    }

在compositionAPI中的计算属性computed中,无法使用mapstate,所以会存在不方便遍历;将遍历封装成一个函数

import { useStore } from "vuex";
import { computed } from "vue";
​
​
export function useMapper(mapper, mapFn) {
  const store = useStore()
​
  const storeStateFns = mapFn(mapper)
​
  const storeState = {}
  Object.keys(storeStateFns).forEach( fnKey => {
    const fn = storeStateFns[fnKey].bind({$store: store})
    storeState[fnKey] = computed(fn)
  })
​
  return storeState
}

可以自己实现一个useState

import { mapState, createNamespacedHelpers } from "vuex";
import { useMapper } from "./useMapper";
export function useState(mapper, moduleName = null) {
​
  let mapperFn = mapState
  if (typeof moduleName === "string" && moduleName.length > 0) {
    mapperFn = createNamespacedHelpers(moduleName).mapState
  }
​
  return useMapper(mapper, mapperFn);
}

getters

getters是vuex中的计算属性,getter同样能够使用内部的函数

getters: {
    // getter同样能够使用内部的函数
    totalPrice(state, getters) {
      const total = state.books.reduce((pre, cur) => {
        return pre += cur.price * cur.count 
      }, 0)
      return total * getters.curDiscount
    },
    curDiscount(state) {
      return state.discount * 0.9
    },
    totalCountGenN(state, getters) {
      return function(n) {
        const total = state.books.reduce((pre, cur) => {
          if (cur.count < n) return pre
          return pre += cur.price * cur.count 
        }, 0)
        return total * getters.curDiscount
      }
    }
  },

配合使用compositionAPI和 封装的函数

useGetters

import { mapGetters, createNamespacedHelpers } from "vuex";
import { useMapper } from "./useMapper";
export function useGetters(mapper, moduleName = null) {
  let mapperFn = mapGetters;
  if (typeof moduleName === "string" && moduleName.length > 0) {
    mapperFn = createNamespacedHelpers(moduleName).mapGetters;
  }
  return useMapper(mapper, mapperFn);
}
  setup() {
    const storeGetters = useGetters(["totalPrice", "totalCountGenN"])
    return {
      ...storeGetters
    }
  }

mutations

mutations存放对于state的修改函数,修改state中的状态值,有且仅有一种方法,通过mutations进行修改。

  • mutations中的方法,第一个参数默认是state,后面自定义传入的值
  • mutations的方法名可以通过常量定义,由于多处使用,避免出现重复更改。
export const INCREMENT_N = "increment_n"
  // 只能使用同步方法,并且只能通过里面的方法更改state的值
  // 缘由:通过监听mutations感知state的变化,异步操作无法及时回显
  mutations: {
    increment(state) {
      state.counter ++
    },
    decrement(state) {
​
      state.counter --
    },
    [INCREMENT_N](state, payload) {
      state.counter += payload.num
    }
  },

组件中使用:使用commit进行调用

import { INCREMENT_N } from "../store/mutations_type"
import { mapMutations } from "vuex"
  export default {
    methods: {
      addTen() {
        // this.$store.commit('increment', 10)
        // 另一种提交风格
        this.$store.commit({
          type: INCREMENT_N,
          num: 11
        }) 
      },
      // 只适用于不传参数的方法
      // 绑定到元素上默认会传一个事件对象
      // 需要传值时需要传入参数
      ...mapMutations(["increment", "decrement", INCREMENT_N])
    },
  }
</script>

actions

actions是处理异步的方法

  // 异步操作聚集
  // 也可以在组件内部调用;也可以在组件内完成异步操作再调用mutations
  actions: {
    incrementActions(context) {
      // console.log(context);
      // context是一个对象,拥有commit getters dispatch等方法
      setTimeout(() => {
        context.commit("increment")
      }, 1000)
    }
  },

组件内使用: 需要通过dispatch方法进行匹配

<script>
import { mapActions} from "vuex"
  export default {
    methods: {
      add() {
        this.$store.dispatch("incrementActions")
      },
      ...mapActions(["incrementActions"])
    },
  }
</script>

modules

避免所有状态存在一起,让文件显的杂乱且繁多,使用模块化的思想,将不同类型数据放在一起,能够集中处理。

例如 user模块

const user = {
  // 命名空间,在获取该对象的数据时,需要指定模块名
  // 拥有更强的封装性
  namespaced: true,
  state() {
    return {
      username: "vue3 User",
      level: "10085",
    };
  },
  getters:{
    levelInfo(state) {
      if (state.level > 10000) return "至尊VIP"
      else return "普通VIP"
    }
  },
  mutations: {
    downLevel(state, payload) {
​
    }
  },
  actions: {
    // context的属性中rootState、rootGetters能够获取到根节点的属性
  }
};
​
export default user
import user from "./modules/user"
​
modules: {
    user
 }

组件中使用模块中的数据

<template>
  <div>
    <h2>当前计数:{{$store.state.counter}}</h2>
    <h2>当前用户:{{username}}</h2>
    <hr>
    <!-- 使用命名空间,该方法获取不到了 -->
    <h2>用户身份:{{$store.getters.levelInfo}}</h2>
    <!-- 需要指定对应的模块 -->
    <h2>用户身份:{{$store.getters["user/levelInfo"]}}</h2>
​
  </div>
</template><script>
import { createNamespaceHelpers } from "vuex"
// 3. 
// 这种方式能够获取到指定模块的内容
const { mapState, mapGetters, mapMutations, mapActions} = createNamespaceHelpers("user")
  export default {
    computed: {
      // 1. 正常使用
      // ...mapState({
      //   username:state => state.user.username
      // })
      // ...mapGetters({
      //   level: "levelInfo"
      // })
​
      // 2. 传入模块
      // ...mapState("user", ["username", "level"]),
      // ...mapGetters("user", ["levelInfo"])
​
      // 3. api
      ...mapState(["username"]),
      ...mapGetters(["increment"])
    }
  }
</script>