温馨提示版本信息: "vue": "^3.0.0", "vuex": "^4.0.2"
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>