3.vuex

安装

npm install vuex@next --save
yarn add vuex@next --save

核心概念

vuex

  • 每一个Vuex应用核心是store,而store就是一个容器,包含了应用中大部分的状态
  • 由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可
  • 应用层级的状态应该集中到单个 store 对象中。
  • 提交 mutation 是更改state的唯一方法,并且这个过程是同步的。
  • 异步逻辑都应该封装到 action 里面。

最简单的store

<div id="app">
    {{count}} <button @click="incrementButton">increment</button>
</div>
const {createApp, computed,ref} = Vue;
const {createStore,useStore } = Vuex;
const store = createStore({
    state(){
        return {
            count: 50
        }
    },
    mutations:{
        increment:state => {
            state.count++
        }
    }
});
const increMachine = function(props,context){
    let count = computed(function(){
        return store.state.count;
    });//store.state.count;
    const incrementButton = () => {
        store.commit('increment');
        console.log(store.state.count)
    }
    return {count,incrementButton}
}
const app = createApp({
    setup(props,context){
        const incDecObject = increMachine(props,context);
        return incDecObject;
    }
});
app.use(store);
app.mount("#app");

State

由于store是响应式的,所以只需要在计算属性中返回某一个状态。

setup(props,ctx){
    let count = computed(store.state.count,function(){
        return store.state.count;
    })
}

但是这种方式的问题在于:如果存在了非常多的状态需要注入,就很麻烦。可以通过mapState函数来生成计算属性。

mapState

mapState函数的可以接受⼀个对象 Object <string|function>

export default{
    computed: {
        ...mapState({
            count: state => state.count,
            user: state => state.user
        });
        //或者 mapState(['count','user'])
    }
}

新的问题是如何setup中使用mapState呢,因为computed只能单个监控?——只能封装了!

function useMapState(...getKeys){
    const storeMapObject = {};
    getKeys.forEach(keys => {
        const oldKey = keys[0];
        const newKey = keys[keys.length-1];
        storeMapObject[newKey] = state => state[oldKey];
    });
    const store = useStore();
    const storeState = {};
    const storeFns = mapState(storeMapObject);
    Object.keys(storeFns).forEach((fnKeys) => {
        const fn = storeFns[fnKeys].bind({ $store: store })
        storeState[fnKeys] = computed(fn);
    })
    return storeState;
}

const app = createApp({
    setup(props,ctx){
        const storeState = useMapState(["count"],["user","username"]);
        return {...storeState};
    }
})

Getter

可以认为是store的计算属性,v3中但不会缓存结果,参数为:stategetters

getters: {
  // getter 返回一个函数
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

通过属性访问

computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}

通过方法访问

store.getters.getTodoById(2)

mapGetters

computed: {
  ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
  ])
}

在setup中使用

export const useGetters = (...keys) => {
    const mapObject = {};
    keys.forEach(key => {
        const oldKey = key[0];
        const newKey = key[key.length - 1];
        mapObject[newKey] = oldKey;
    });
    const storeFns = mapGetters(mapObject);
    return mapResult(storeFns);
};
function mapResult(storeFns){
    const results = {};
    const store = useStore();
    Object.keys(storeFns).forEach(storeFnsKey => {
        const storeFn = storeFns[storeFnsKey].bind({$store : store});
        results[storeFnsKey] = computed(storeFn);
    });
    return results
}

Mutation

更像是事件注册,store.commit('increment')唤醒一个mutation处理函数。mutation接受 state 作为第一个参数。Mutation中不能是同步函数,任何在回调函数中进行的状态的改变都是不可追踪的。

提交载荷(Payload)

传入额外的参数,即 mutation 的载荷(payload),例如store.commit('increment', 10)

而载荷应该是一个对象。

mutations:{
    increment: (state,payload){
        return state.count += payload.count;
    }
}
store.commit("increment",{count:10})

对象风格的提交方式

store.commit({
	type: 'increment',
	amount: 10
});

常量替代 Mutation 事件类型

//mutations_types.js
export const INCRMENT = "increment";
//store.js
import {INCREMENT} from "./mutations_types";
const store = createStore({
    state(){return{};},
    getters:{},
    mutations:{
        //ES2015 风格的计算属性命名
        [INCREMENT](state)=>{
			state.count++
        }
    }
});

提交 Mutation

this.$store.commit('increment')

可以不使用this.$store.commit,使用mapMutations

mapMutations

methods:{
	...mapMutations(['increment','incrementBy']),
	...mapMutations({add:"increment"});
}

在setup中使用mapMutations

<button @click="add">add</button>
function useMapMutations(...keys){
    const mapObject = {};
    keys.forEach(key => {
        const newKey = key[0];
        const oldKey = key[key.length - 1];
        mapObject[oldKey] = newKey;
    });
    const storeFns = mapMutations(mapObject);
    const store = useStore();
    const results = {};
    Object.keys(storeFns).forEach(fnKey => {
        results[fnKey] = storeFns[fnKey].bind({ $store: store });
    });
    return results;
}


const app = createApp({
    setup(props,cxt){
        const mutations = useMapMutations(['increment','add']);
        return { ...mutations };
    }
})

Action

mutation中不能进行异步操作,mutation是直接变更状态。

const store = creatStore({
    state(){
        return {
            count: 0;
        };
    },
    getters:{},
    mutations:{
        add: () => {
			count++;
        }
    },
    actions:{
        increment:(context) => {
			context.commit('add');
        }
    }
});

分发Action

action中可以进行异步操作。

store.dispatch('increment');
actions:{
    incremrntAsync: ({commit}) => {
        setTimeout({
            commit("add",50);
        },1000);
    }
}

支持载荷

incremrntAsync: ({commit},count) => {});
//或者
incremrntAsync: ({commit},{count}) => {});

mapActions

第一个参数是与store相同属性的context,第二个参数是载荷。

function useMapActions(...keys){
    const mapObject = {};
    keys.forEach(key => {
        const oldKey = key[0];
        const newKey = key[key.length - 1];
        mapObject[newKey] = oldKey;
    });
    const mapFns = mapActions(mapObject);
    const store = useStore();
    const results = {};
    Object.keys(mapFns).forEach(fnKey => {
        results[fnKey] = mapFns[fnKey].bind({ $store: store });
    });
    return results;
}
const store = createStore({
    state(){
        return {
            count: 0
        }
    },
    getters:{}.,
    mutations:{
        increment(state,{count}){
            state.count += count;
        }
    },
    actions:{
        increment({commit},{count}){
            //模拟异步
            setTimeout(() => {
                commit("increment",{count});
            },1000);
        }
    }
});
const app = createApp({
    setup(props,ctx){
        const state = useMapState(['count']);
        const actions = useMapActions(['increment','add']);
        return {...state,...actions};
    }
})
{{count}}
<button @click="() => add(100)">
    +
</button>

Module

当数据庞大时,进行分模块。

const moduleA = {
    state: () => {retunr {};},
    mutations:{},
    actions:{},
    getters:{}
};
const moduleB = {
    state: () => {retunr {};},
    mutations:{},
    actions:{},
    getters:{}
};
const store = createStore({
    modules:{
        a: moduleA,
        b: moduleB
    }
});
//查看信息 store.state.a

模块的局部状态

  • 每个某块内部的mutaionsgetters中的第一个参数state是**当前模块的state**。

  • 模块中的actions的参数context(==store)包括了本块中的state全局的rootState

    const modukeA = {
        state(){
            return {
                count : 100
            };
        },
        actions:{
            increment({state,rootState}){
                console.log(state,rootState);
            }
        }
    }
    

命名空间

  • 每个某块内部的mutations,getters,actions都是默认注册在全局命名空间

    const teacher = {
        state(){
            return {
                name: 'Miss Zhang'
            };
        },
        getters:{
            helloTeacher:(state) => {
                return `hello, ${state.name}`
            }
        }
    }
    const store = createStore({
        state(){
            return {
                name : 'chenxin'
            };
        },
        modules:{
            teacher
        }
    });
    const app = createApp({
        setup(props,context){
            const getters = useMapGetters(['helloTeacher']); //hello, Miss Zhang
            return {...getters };
        }
    });
    

提高封装性和复用性

在某一个模块中添加namespaced:true

const teacher = {
    namespaced: true,
    state: () => ({
        name: 'Miss Zhang'
    }),
    getters:{
        helloTeacher:(state) => {
            return `hello, ${state.name}`
        }
    }
}
//可以通过 $store.getters['/teacher/helloTeacher']
//或者 mapGetters('teacher',{helloTeacher})
//或者 mapGetters('teacher/helloTeacher')
//在setup中使用——看例子代码useMapGetters('teacher',['helloTeacher'])
module嵌套
const teacher = {
    namespaced:true,
    modules:{
        headmaster:{
    		namespaced:true,
            state: () => ({name:'chenxin'}),
            getters:{
                helloHeadmaster:(state) => {
                    return `hello, ${state.name}`
                }
            }
        }
    }
}
//可以通过 $store.getters['/teacher/headmaster/helloHeadmaster']

//或者 mapGetters('teacher',{'helloHeadmaster':‘headmaster/helloHeadmaster’})

//或者 mapGetters('teacher/headmaster',['helloHeadmaster'])

//在setup中使用——看例子代码useMapGetters('teacher',['headmaster/helloHeadmaster','helloHeadmaster'])
命名空间访问全局内容

getters的参数**state,getters,rootState,rootGetters**

modules:{
    people:{
        modules:{
            man:{
                getters:{
                    name:(state,getters,rootState,rootGetters) => {
                        return 'something';
                    }
                },
                actions:{
                    action1:({state,getters,rootState,rootGetters}) => {
                        do something;
                    }
                }
            }
        }
    }
}
在【命名空间】注册【全局】action

添加 root: true

modules:{
    people:{
        modules:{
            man:{
                actions:{
                    action1:{
                        root: true,
                        handler: (cxt,payload) => {
                            do something;
                        }
                    }
                }
            }
        }
    }
}

动态注册

使用store.registerModule('newModuleName',oldModule,parameter<Object>)注册模块

store.registerModule('myModule',{
   state: () => ({}),
   //...
    
});

store.registerModule(['mainModule','subModule'],{
   state: () => ({}),
   //...
});

使用store.unregisterModule(moduleName | ['mainModule','subModule'])卸载模块

使用store.hasModule(moduleName | ['mainModule','subModule'])是否有这个模块

preserveState
  • 当没有使用preserveState
const teacher = {
    modules:{
        headmaster:{
            namespaced: true,
            state: () => ({
                name: 'wangwu'
            }),
            getters:{
                helloHeadMaster: (state,getters,rootState,rootGetters) => {
                    return `hello, ${state.name}`
                }
            }
        }
    }
};
const store = createStore({
    modules:{
        teacher
    }
});

const createDynamicModule = function(props,ctx){
    store.registerModule(['teacher','leader'], teacher.modules.headmaster);
    const leader = ref(store.getters['teacher/leader/helloHeadMaster']);
    return { leader };
}

const app = createApp({
    setup(props,context){
        const leader = createDynamicModule(props,context);
        return { ...leader };
    }
});

//leader:hello, wangwu
  • 当使用preserveState
store.registerModule(['teacher','leader'], teacher.modules.headmaster, { preserveState: true });

image-20220717131625888

结论:{ preserveState: true }保存原module的getters,mutations,actions但是不保存state,即新module的state为空

模块重用

当模块的声明是一个对象时,当被多个store所注册,某个store中的修改将可能影响多个store,类比如为什么createApp({data:() => ({})})data是一个函数类型,也就是形成了闭包。所以解决办法也是相同的——使用一个函数来声明模块状态

plugins

内置 Logger 插件

const {createStore,createLogger} = store;
const store = createStore({
	state:()=>({}),
	mutations:{},
	plugins:[createLogger()]
});

严格模式

strict : true,只允许commit mutations中修改state。这能保证所有的状态变更都能被调试工具跟踪到。不要在发布环境下启用严格模式!
所以可以加判断
strict: process.env.NODE_ENV !== 'production'

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值