vuex是一个专门为vue应用程序管理提供状态管理的库,作用就是存储组件共用的属性,也就是state,并以相应的规则保证状态以一种可预测的方式发生变化
大家可以思考一下,组件之间的传值有哪些?有父子通讯,兄弟组件通讯......但是传参对于多层嵌套就显得非常繁琐
代码维护也会非常麻烦。因此vuex就是把组件共享状态抽取出来以一个全局单例模式管理,把共享的数据函数放进vuex中,任何组件都可以进行使用
引用官网的一句话:如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,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 下面进行详细讲解
提供唯一的公共数据源,所有共享的数据统一放到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}}
在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和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);
}
}
类似于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'])
},
当遇见大型项目时,数据量大,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);