VUEX
组件获取状态(State)
如果我们有很多个状态都需要获取话,可以使用mapState
的辅助函数:
import { mapState } from 'vuex
mapState返回的是一个对象,加’…'表示将函数内容变成computed格式,即此时返回的是一个个函数,computed返回的是ref对象
option api使用
- mapState的方式一:对象类型;
...mapState({
sCounter:state => state.counter,
sName: state => state.name
})
- mapState的方式二:数组类型;
...mapState(["counter", "name"])
- 也可以使用展开运算符和来原有的computed混合在一起;
composition api使用
在setup中如果我们单个获取装是非常简单的:
- 通过useStore拿到store后去获取某个状态即可
import { useStore } from 'vuex';
import { computed } from 'vue';
setup(){
const store = useStore();
const sCounter = computed(()=> store.state.counter);
}
setup(){
const state = useStore({
name: state => state.name,
age: state => state.age
})
return {
...state
}
}
- 使用mapstate
setup(){
const store = useStore();
const storeStateFns = mapState(["counter", "name", "age"]);
const storeState = {};
Object.keys(storeStateFns).forEach(fnKey => {
const fn = storeStateFns[fnKey].bind({$store:store});
storeState[fnKey] = computed(fn)
})
return{
...storeState
}
}
bind的用法举栗:
function abc(){
console.log(this);
}
cosnt foo = abc.bind({name:"hahaha"});
foo();
封装
import { useState } from '../hooks/useState'
export default {
setup(){
const storeState = useState(["counter", "name"])
return{
...storeState
}
}
}
---------useState.js
import { useStore, mapState } from 'vuex';
import { computed } from 'vue';
export function useState(mapper){
const store = useStore();
const storeStateFns = mapState(["counter", "name", "age"]);
const storeState = {};
Object.keys(storeStateFns).forEach(fnKey => {
const fn = storeStateFns[fnKey].bind({$store:store});
storeState[fnKey] = computed(fn)
})
return storeState
}
getters
某些属性我们可能需要警告变化后来使用,这个时候可以使用getters
option API
computed:{
...mapGetters(["totalPrice", "myName"]),
...mapGetters({
finalPrice: "totalPrice",
finalName: "myName"
})
}
composition api
import{ useStore, mapGetters } from 'vuex';
import{ computed } from 'vue';
export function useGetters(mapper){
const store = useStore();
const stateFns = mapGetters(mapper);
const state = {}
Object.keys(stateFns).forEach(fnKey => {
const fn = stateFns[fnKey].bind({$store:store});
state[fnKey] = computed(fn)
})
return state
}
Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
mutations:{
add(state, payload){
state.counter += payload.count
}
}
payload为对象类型
对象风格的提交方式
$store.commit({
type:"add",
count:100
})
mapMutations
此时setup和methods的使用方法一致,可以帮助我们快速映射到对应的方法中
...mapMutations({
add: "add"
})
//...mapMutations(["add"])
Mutation重要原则
一条重要的原则就是要记住 mutation 必须是同步函数
- 这是因为devtool工具会记录mutation的日记;
- 每一条mutation被记录,devtools都需要捕捉到前一状态和后一状态的快照;
- 但是在mutation中执行异步操作,就无法追踪到数据的变化;
- 所以Vuex的重要原则中要求 mutation必须是同步函数
action
Action类似于mutation,不同在于:
- Action提交的是mutation,而不是直接变更状态;
- Action可以包含任意异步操作
mutations:{
increment(state){
state.counter++;
//可以直接改变state数据
}
}
actions:{
increment(context){
context.commit("increment");
//要通过提交事件触发mutation改变状态
}
}
actions的分发操作
分发使用的是 store 上的dispatch函数
add(){
//this.$store.dispatch("increment");
//this.$store.dispatch("increment",{counter:100});
this.$store.dispatch({
type:"increment",
count:100
});
}
actions的辅助函数
- option API
methods:{
...mapActions(["increment"]),
...mapActions({
add:"increment"
})
}
- composition API
const actions1 = mapActions(["increment"]);
const actions2 = mapActions({
add:"increment"
});
actions的异步操作
可以通过让action返回Promise,在Promise的then中来处理完成后的操作
actions:{
increment(context) {
return new Promise((resolve) => {
setTimeout(() =>
context.commit("increment")
resolve("异步完成")
},1000);
})
}
}
const store = useStore();
const increment = ()=>{
store.dispatch("increment").then(res=>{
console.log(res, "异步完成")
})
}
action的第一个context参数可以解构成:
({commit, dispatch, state, rootState, getters, rootGetters})
getter有四个参数:
(state, getters, rootState, rootGetters)
如果希望在action中修改root中的state:
commit("changeName", null, {root:true})
//此时第二个参数是外来参数,是一个对象
dispatch("changeName", null, {root:true})
module
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象
命名空间
默认情况下,模块内部的action和mutation仍然是注册在全局的命名空间中的:
- 这样使得多个模块能够对同一个 action 或 mutation 作出响应;
- Getter 同样也默认注册在全局命名空间;
但此时会出现同名函数或变量同时响应,所以可以采用命名空间来使模块具有更高的封装度和复用性。
---------可以添加namespaced: true
的方式使其成为带命名空间的模块: - 当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名
辅助函数
module的辅助函数mapState,mapGetters,mapMutations,mapActions
有三种使用方法:
- 方式一:通过完整的模块空间名称来查找;
computed:{
...mapState({
a:(state) => state.some.nested.module.a,
})
},
methods:{
..mapActions([
"some/nested/module/foo",
])
}
- 方式二:第一个参数传入模块空间名称,后面写上要使用的属性;
computed:{
...mapState("some/nested/module",{
a:(state) => state.a,
})
},
methods:{
..mapActions("some/nested/module",[
"foo",
])
}
- 方式三:通过 createNamespacedHelpers 生成一个模块的辅助函数
import {mapState, mapActions} = createNamespacedHelpers('some/nested/module')
export default{
computed:{
...mapState({
a:(state) => state.a,
})
},
methods:{
..mapActions([
"foo",
])
}
}
composition API写法:
setup(){
<!-- const state = mapState(["homeCounter"]) -->
<!-- const getters = mapGetters(["doubleHomeCounter"]) -->
<!-- 这里由于前面已经封装过对应的hook所以只要引入使用即可,最好给第一个参数指明模块 -->
const state = useState(["home","homeCounter"])
const getters = useGetters(["home","doubleHomeCounter"])
const mutations = mapMutations(["increment"])
const actions = mapActions(["incrementAction"])
return{
...state,
...getters,
...mutations,
...actions
}
}
useState和useGetters支持模块化封装
前面封装的hook是不能对模块使用的
<!-- useMapper.js -->
import {computed} from 'vue'
import {mapState, useStore} from 'vuex'
export function useMapper(mapper, mapFn){
<!-- 拿到store对象 -->
const store = useStore()
<!-- 获取到对应的对象的function {name: function, age:function} -->
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的修改
<!-- useState.js -->
import { mapState, createNamespacedHelpers } from 'vuex'
import { useMapper } from './useMapper'
export function useState(moduleName, mapper){
let mapperFn = mapState
if(typeof moduleName === 'string' && moduleName.length>0) {
mapperFn = createNamespacedHelpers(moduleName).mapState
}
return useMapper(mapper,mapperFn)
}
useGetters的修改
<!-- useGetters.js -->
import { mapGetters, createNamespacedHelpers } from 'vuex'
import { useMapper } from './useMapper'
export function useGetters(moduleName, mapper){
let mapperFn = mapGetters
if(typeof moduleName === 'string' && moduleName.length>0) {
mapperFn = createNamespacedHelpers(moduleName).mapGetters
}
return useMapper(mapper,mapperFn)
}