vuex详解

概述

vuex是一个专门为vue应用程序管理提供状态管理的库,作用就是存储组件共用的属性,也就是state,并以相应的规则保证状态以一种可预测的方式发生变化

大家可以思考一下,组件之间的传值有哪些?有父子通讯,兄弟组件通讯......但是传参对于多层嵌套就显得非常繁琐

代码维护也会非常麻烦。因此vuex就是把组件共享状态抽取出来以一个全局单例模式管理,把共享的数据函数放进vuex中,任何组件都可以进行使用

选择与使用

引用官网的一句话:如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择

vuex安装

 命令安装

npm install vuex@next --save

或者

yarn add vuex@next --save

使用vue-cli创建项目时选择vuex依赖,自动化安装,建议使用这种,避免出现问题

 在src下建立store文件夹,在store文件夹下建立index.js,内容如下

import {createStore} from 'vuex';
 
 
export default createStore({
  //数据,相当于data,共享数据
  state: {
    
  },
  getters: {
    
  },
  //里面定义方法,操作state共享数据的方法在这里调用
  mutations: {
    
  },
  // 操作异步操作mutation
  actions: {
    
  },
  modules: {
    
  },
})

在main.js中引入store,使用vuex进行组件间共享数据管理

核心概念

vuex中一共有五个状态 State  Getter  Mutation   Action   Module  下面进行详细讲解

State

提供唯一的公共数据源,所有共享的数据统一放到store的state进行储存,与data相似

在各个组件中都可以调用操作共享数据,类似react的dva状态管理,基本就是一个东西

 在标签中直接使用

在标签中不用指定this 当前就是this

{{$store.state.name}}

this.$store.state.全局数据名称

使用mapstate辅助函数

import { mapState } from "vuex";


computed:{
    // mapState辅助函数 解构需要的共享数据
    ...mapState(['name','age']);
}


// 使用数据

{{name + age}}

Mutation

在vuex中Mutation是唯一一个能够修改state数据的地方,Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数

import {createStore} from 'vuex';

export default createStore({
  //数据,相当于data,共享数据
  state: {
    name :'tom',
    age :'',
    count:0,
  },
  getters: {
    
  },

  mutations: {
    // 第二个参数num是传进来的调用参数
    add(state,num){
        state.count+= num;
    },
    
    reduce(state){
        state.count --;
    }
    
  },
  // 操作异步操作mutation
  actions: {
     
  },
  modules: {
    
  },
})

在组件中使用也有两种方式

commit方法出发mutation

methods:{
//加法
btn(){
    this.$store.commit("addcount",10)   
}
//减法
btn1(){
    this.$store.commit("reduce") 
}
}

 使用辅助函数mapMutations进行操作

methods:{
 ...mapMutations(['addcount','reduce']),
//加法
btn(){
    this.addcount(10);   
}
//减法
btn1(){
    this.reduce();
}
}

Action 

Action和Mutation相似,一般不用Mutation 异步操作,若要进行异步操作,使用Action

原因:为了方便devtools打个快照存下来,方便管理维护。所以说这个只是规范,而不是逻辑的不允许,只是为了让这个工具能够追踪数据变化而已,一般接口的定义在这个位置

context的参数,相当于上下文

    {
        state,   等同于store.$state,若在模块中则为局部状态
        rootState,   等同于store.$state,只存在模块中
        commit,   等同于store.$commit
        dispatch,   等同于store.$dispatch
        getters   等同于store.$getters
    }
import {createStore} from 'vuex';

export default createStore({
  //数据,相当于data,共享数据
  state: {
    name :'tom',
    age :'',
    count:0,
  },
  getters: {
    
  },

  mutations: {
    // 第二个参数num是传进来的调用参数
    add(state,num){
        state.count+= num;
    },
    
    reduce(state){
        state.count --;
    }
    
  },
  // 操作异步操作mutation
  actions: {
     asyncAdd({dispatch,commit}){
        // 异步操作
        setTimeout(()=>{
           commit('add',num);
        },1000);    
     }
  },
  modules: {
    
  },
})

 在组件中使用有两种方式

 直接使用dispatch触发函数

this.$store.dispatch("asynAdd")

使用辅助函数mapActions操作

methods:{
 ...mapActions(['asyncAdd']),
//加法
btn(){
    this.asyncAdd(10);   
}
}

Getter

类似于vue中的computed,进行缓存,对于Store中的数据进行加工处理形成新的数据,比如state中的列表,要进行筛选,可以使用getter进行fiter操作, 函数接受两个参数,分别是state,getters


state:{
    students:[
        {name : "tom",age:11},
        {name : "tom",age:16},
    ]
},
getters: {
    studFilter(state) {
      return state.students.filter(item => item.age > 15);
    }
    stuLength(state, getters) {
      return getters.studFilter.length;
    }

  },

getter不能接受参数,如果想接受参数,需要让geter返回另一个函数

getters: {
    moreAgestu(state) {
      return Age => {
        return state.students.filter(item => item.age > Age);
      }
    }
  },

 在组件中使用

 直接调用

{{this.$store.getters.moreAgestu(10)}}

使用辅助函数mapGetters

 computed: {
      ...mapGetters(['moreAgestu'])
 },

Modules 

当遇见大型项目时,数据量大,store就会显得很臃肿

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter

注意

state为自己私有,不注册到全局,默认情况下,所有模块的getters、mutations和actions都是注册到全局的

这样如果多个子模块的getters、mutations和actions中有同名时候,会导致覆盖,引起问题。因此通常我们需要给子模块加命名空间,给子模块加命名空间的方式是给子模块加namespaced属性并赋值为true

嵌套模块展示

import {createStore} from 'vuex';
const moduleA = {
    state: {
        name: 'a'
    }
};

const moduleB = {
    state: {
        name: 'b'
    }
};

const store = createStore({
    modules: {
        a: moduleA,
        b: moduleB
    }
});

// 访问的时候带模块名称
console.log(store.state.a.name); // a
console.log(store.state.b.name); // b

嵌套模块访问方法和属性

import {createStore} from 'vuex';

const store = createStore({
    state: {
        counter: 0
    },
    getters: {
        counter10times(state) {
            return state.counter * 10;
        }
    },
    modules: {
        a: {
            state: {aName: 'A·a'},
            aGetters: {
                aFirstName(state) {
                    return state.aName.split('·')[0];
                }
            },
            modules: {
                c: {
                    state: {cName: 'C·c'},
                    getters: {
                        cFirstName(state) {
                            return state.cName.split('·')[0];
                        }
                    }
                }
            }
        },
        b: {
            state: {bName: 'B·b'},
            getters: {
                bFirstName(state) {
                    return state.bName.split('·')[0];
                },
                bNewName(state, getters, rootState, rootGetters) {
                    // 访问局部state
                    const {bName} = state;
                    // 访问全局state
                    const {a: {c: {cName}}} = rootState;
                    // 访问局部getters
                    const {bFirstName} = getters;
                    // 访问全局getters
                    const {cFirstName} = rootGetters;
                    return `${bName}  ${bFirstName}  ${cName}  ${cFirstName}`;
                }
            }
        }
    }
});

// 子模块的state通过子模块路径访问
console.log(store.state.a.c.cName);

// 子模块的getters都注册到了全局,在store.getters下面直接能访问到
console.log(store.getters.bNewName);

加入命名空间访问state和getter

import {createStore} from 'vuex';

const store = createStore({
    state: {
        counter: 0
    },
    getters: {
        counter10times(state) {
            return state.counter * 10;
        }
    },
    modules: {
        a: {
            namespaced: true,
            state: {aName: 'A·a'},
            getters: {
                aFirstName(state) {
                    return state.aName.split('·')[0];
                }
            },
            modules: {
                c: {
                    namespaced: true,
                    state: {cName: 'C·c'},
                    getters: {
                        cFirstName(state) {
                            return state.cName.split('·')[0];
                        }
                    }
                }
            }
        },
        b: {
            namespaced: true,
            state: {bName: 'B·b'},
            getters: {
                bNewName(state, getters, rootState, rootGetters) {
                    // 局部state
                    const bName = state.bName.split('·')[0];
                    // 其他模块的getter
                    const cFirstName = rootGetters['a/c/cFirstName'];
                    // 其他模块的state
                    const aName = rootState.a.aName;
                    return `${bName} ${cFirstName} ${aName}`;
                }
            }
        }
    }
});

// getters命名空间
console.log(store.getters['b/bNewName']); // B C A·a

// 子节点state仍然是通过节点路径访问
console.log(store.state.a.c.cName); // C·c

加入命名空间访问commit mutations 和dispatch actions

import {createStore} from 'vuex';

const store = createStore({
    state: {
        counter: 0
    },
    mutations: {
        increaseCounter(state) {
            state.counter++;
        }
    },
    modules: {
        a: {
            namespaced: true,
            state: {aName: 'A·a'},
            mutations: {
                changeAName(state) {
                    state.aName = 'A-a';
                }
            },
            actions: {
                callChangeCNameAsync({dispatch}) {
                    // 触发子模块的action,是相对于自身的路径,不需要加a前缀
                    setTimeout(() => {
                        dispatch('c/changeCNameAsync');
                    }, 500);
                }
            },
            modules: {
                c: {
                    namespaced: true,
                    state: {cName: 'C·c'},
                    mutations: {
                        changeCName(state, payload) {
                            state.cName = `C-c-${payload.suffix}`;
                        }
                    },
                    actions: {
                        changeCNameAsync({commit, rootState}) {
                            setTimeout(() => {
                                // 提交其他模块的mutation,mutation是全局的
                                commit('increaseCounter', null, {root: true});
                                // 提交局部模块的mutation,不需要加前缀
                                commit('changeCName', {
                                    suffix: rootState.counter
                                });
                            }, 500);
                        }
                    }
                },
            }
        },
        b: {
            namespaced: true,
            state: {bName: 'B·b'},
            mutations: {
                changeBName(state) {
                    state.bName = 'B-b';
                }
            }
        }
    }
});

// 全局的commit
// 注意加了命名空间之后,提交根模块的mutation和触发根模块的action时候,都需要加上{root: true}的选项
store.commit('increaseCounter', null, {root: true});
console.log(store.state.counter); // 1

// 子模块mutation注册到全局了
store.commit('a/c/changeCName', {suffix: '123'});
console.log(store.state.a.c.cName); // C-c-123

// 子模块commit其他模块的mutation
store.dispatch('a/c/changeCNameAsync');
setTimeout(() => {
    console.log(store.state.a.c.cName); // C-c-2
}, 1000);

// 子模块dispatch其它模块的action
store.dispatch('a/callChangeCNameAsync');
setTimeout(() => {
    console.log(store.state.a.c.cName); // C-c-3
}, 1500);

你可能感兴趣的:(前端星球,前端,javascript,开发语言)