安装
npm install vuex@next --save
yarn add vuex@next --save
核心概念
- 每一个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中但不会缓存结果,参数为:state
,getters
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
模块的局部状态
-
每个某块内部的
mutaions
和getters
中的第一个参数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 });
结论:{ 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'