vuex是一个状态(数据)管理工具,它能够完成组件之间的数据共享(组件的通信)。
一.vuex的作用
1、vuex能够保存全局数据(数据仓库),供整个应用使用
2、vuex保存的数据是响应式的
3、vuex保存的数据可以跟踪状态的变化
二.vuex的核心概念(创建vuex.store对象里的配置项)
state : 数据仓库 ,存储所有的 共享数据 ,相当于vue组件里的data。
Getter : 在state的基础上 派生的数据, 相当于vue组件里 computed。
Mutation:修改state的数据时,用mutation,这与跟踪状态 有关系,只能有同步代码。
Action:解决mutation里只能有同步代码的问题,action里可以有异步代码。
modules:模块化。
三.vuex的基本使用
①安装
npm安装vueX: npm install vuex --save
②创建vuex对象
./src/store/index.js
import Vue from 'vue'
//引入vueX
import Vuex from 'vuex'
//把vueX安装到Vue里
Vue.use(Vuex)
export default new Vuex.Store({
state:{
id: '01'
},
getters:{},
mutations:{},
actions:{}
})
③把vuex.store对象植入到 vue的根属性
./src/main.js
import store from './store'
new Vue({
el: '#app',
store,//把store对象植入到vue的根属性,在vue组件里就可以使用 this.$store拿到vueX.store对象
router
……………………
})
④组件里获取数据
//模板里:
$store.state.id
//脚本里
this.$store.state.id
⑤组件里保存数据
this.$store.state.id = '02'
//这个方式虽然可以,但是不推荐,因为,它不能跟踪状态。
//推荐,强烈推荐(必须)使用mutation来修改数据。
四:vuex的核心概念详解
state:数据仓库 ,存储所有的 共享数据 ,相当于vue组件里的data,是一个单一状态树
使用: 在组件中使用:this.$store.state.属性名。
示例:
export default new VueX.Store({
state:{
age:12,
isAdult:"未成年",
isAllowMarry:false
}
…………………………
});
组件里获取:
{{$store.state.age}}
{{$store.state.isAdult}}
{{$store.state.isAllowMarry?"可以结婚了":"不要着急,再等等"}}
Getter : 在state的基础上 派生的数据, 相当于vue组件里 computed
示例:
export default new VueX.Store({
state:{
age:12,
isAdult:"未成年",
isAllowMarry:false
},
getters:{
// state:就是仓库的state,不用程序员处理,vuex已经处理好了
ageChina:function(state){
let shi = parseInt(state.age/10); //1
let ge = state.age%10;//2
let str = "";
switch(shi){
case 1:str='一';break;
case 2:str='二';break;
}
str+='十'
switch(ge){
case 1:str+='一';break;
case 2:str+='二';break;
case 3:str+='三';break;
case 4:str+='四';break;
case 5:str+='五';break;
case 6:str+='六';break;
case 7:str+='七';break;
case 8:str+='八';break;
case 9:str+='九';break;
}
return str+'岁';
}
},
…………………………
});
组件里获取:
{{$store.getters.ageChina}}
Mutation:修改state的数据时,用mutation,这与跟踪状态 有关系。
换句话说,在vuex中,对mutation的定义(定位)是:修改状态的,即:在mutation提交的前后,状态应该是不一样的。当然了,也会在vue的dev-tools工具中看到跟踪状态的效果。
在vuex中,强烈建议(必须)使用mutation改变state中的值。可以在vuex对象中使用严格模式来检测: const store = new Vuex.Store({ strict: true })
示例:
export default new VueX.Store({
state:{
age:12,
isAdult:"未成年",
isAllowMarry:false
},
getters:{
ageChina:function(state){
let shi = parseInt(state.age/10); //1
let ge = state.age%10;//2
let str = "";
switch(shi){
case 1:str='一';break;
case 2:str='二';break;
}
str+='十'
switch(ge){
case 1:str+='一';break;
case 2:str+='二';break;
case 3:str+='三';break;
case 4:str+='四';break;
case 5:str+='五';break;
case 6:str+='六';break;
case 7:str+='七';break;
case 8:str+='八';break;
case 9:str+='九';break;
}
return str+'岁';
}
},
// mutations:是跟踪状态。这里面只能有同步代码,这是必须的。
mutations:{
// incAge(state,num){
// state.age+=num;
//state:就是store对象里的state,不用程序员传入
//payload:就是形参,叫作载荷
incAge(state,payload){
state.age+=payload.num;
if(state.age>=18){
state.isAdult = "已成年";
}else{
state.isAdult = "未成年";
}
if(state.age>=22){
state.isAllowMarry = true;
}else{
state.isAllowMarry = false;
}
}
},
…………………………………………
});
提交mutation:
//组件里提交
this.$store.commit('incAge',num);
//action提交
incAge(context,num){
context.commit('incAge',num);
}
Action:解决mutation里只能有同步代码的问题,action里可以有异步代码
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
示例:
export default new VueX.Store({
state:{
age:12,
isAdult:"未成年",
isAllowMarry:false
},
getters:{
ageChina:state=>{
let shi = parseInt(state.age/10); //1
let ge = state.age%10;//2
let str = "";
switch(shi){
case 1:str='一';break;
case 2:str='二';break;
}
str+='十'
switch(ge){
case 1:str+='一';break;
case 2:str+='二';break;
case 3:str+='三';break;
case 4:str+='四';break;
case 5:str+='五';break;
}
return str+'岁';
}
},
// mutations:是跟踪状态。这里面只能有同步代码,这是必须的。
mutations:{
// incAge(state,num){
// state.age+=num;
incAge(state,payload){
state.age+=payload.num;
if(state.age>=18){
state.isAdult = "已成年";
}else{
state.isAdult = "未成年";
}
if(state.age>=22){
state.isAllowMarry = true;
}else{
state.isAllowMarry = false;
}
}
},
// 如果出现异步操作,就需要使用action,action不是必须的。
actions:{
// incAge(context,num){
// context.commit('incAge',num);
// }
// incAge(context,payload){
// context.commit('incAge',payload);
// }
//context:是store对象
incAge(context,payload){
// context.commit('incAge',payload);
context.commit(payload);
}
}
});
Action和mutation的区别:
在代码的角度上,action是来提交mutation的
在用途(意义)上:区分 actions 和 mutations 并不是为了解决竞态问题,而是为了能用 devtools 追踪状态变化。 vuex 真正限制你的只有 mutation 必须是同步的这一点。 当然actions可以包含任何异步操作,如果程序员自己想处理异步操作,也可以不使用actions。
Modules:当项目比较大时,所有的全局数据存放在state里,会非常混乱,怎么办?使用module,把数据分门别类的进行处理,即:模块化。
每个模块是一个独立的store。然后由总体的store引入所有的分模块store。
如下:两个模块分别管理不同类型的数据
./src/store/moduleA.js
export default {
state: {count:1},
mutations: { ... },
actions: {
incCount(context){
console.log("moduleA的action");
setTimeout(()=>{
context.commit("incCount");
},2000);
}
},
getters: { ... }
}
./src/store/moduleB.js
export default {
state: {age:2},
mutations: { ... },
actions: {
incCount(context){
console.log("moduleB的action");
setTimeout(()=>{
context.commit("incCount");
},2000);
}
},
getters: { ... }
}
在总的store里包含所有的模块:
./src/store/index.js
import Vue from "vue";
import vueX from "vuex";
import moduleA from "./moduleA";
import moduleB from "./moduleB";
Vue.use(vueX);
export default new vueX.Store({
modules:{
moduleA:moduleA,
moduleB:moduleB
}
//简写:
modules:{
moduleA,moduleB
}
});
组件里使用数据时,多加个模块名即可,如:
$store.state.moduleA.count // -> moduleA 的状态
$store.state.moduleB.count // -> moduleB 的状态
注: 组件里派发action(或者提交mutation)时,如果,直接写action(mutation)的名字,那么就会找到所有同名的action(mutation)。(getter也有这个问题)
//如:
//在组件内容,派发action
this.$store.dispatch({
type:"incCount"
});
那么就会,派发给moduleA和moduleB里的incCount。即:moduleA和moduleB里的incCount都被执行了。如果不希望这样,那就用命名空间
模块(Module)里的命名空间(namespaced:true)。
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true
的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
1)、模块定义时,加上namespaced:true
export default {
namespaced:true,
state:{
count:1
},
mutations:{
……………………
},
actions:{
incCount(context){
console.log("moduleA的action");
setTimeout(()=>{
context.commit("incCount");
},2000);
}
}
}
2)、组件里派发action时,加上模块名
this.$store.dispatch('moduleA/incCount');
五:vuex的 数据流转
解读一下: vue组件 派发(dispatch)action。action里提交(commit)mutation,mutation里修改(mutate)state的数据,state的数修改后,会响应式渲染到模板上。
六:vuex的辅助函数
1.mapState
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState
辅助函数帮助我们生成计算属性,让你少按几次键:
mapState辅助函数会把仓库中的状态映射到组件的computed上。
//1、vueX
export default new vueX.Store({
state:{
count:10
}
})
//2、组件里
import { mapState } from "vuex";
export default {
name: "Store01",
data() {
return {};
},
//1)、把vueX中的state用计算属性,很麻烦
// computed:{
// count(){
// return this.$store.state.count
// }
// },
// 2)、使用mapState(不能再写其它计算属性)
// computed: mapState({
// // 箭头函数可使代码更简练
// count: state => state.count,
// }),
// 3)、使用mapState继续简写(不能再写其它计算属性)
// computed: mapState(['count']),
// 4)、使用mapState,这样写
computed:{
...mapState(['count']),
a:function(){
return 250;
},
}
};
mapState映射模块里的数据,:
//直接把整个模块映射过来
//1、计算属性里的映射
computed:{
...mapState([模块名]),
},
//2、模板上的使用
<p>{{模块名.属性}}</p>
2.mapGetters
mapGetters辅助函数,把仓库中的getters映射到组件的computed里。
computed: {
...mapState(["count", "age", "isAdult"]),
...mapGetters(["ageChina"]),
},
mapGetters映射模块里的数据 :
computed:{
...mapGetters({"属性名":"模块名/属性名"}),
...mapGetters({"money":"a/money"}),
},
3.mapMutations
在methods里映射
4.mapActions
在methods里映射
methods: {
...mapMutations(["incAge1"]),
...mapActions(["incAge"]),
}
mapMutations映射模块里的方法(mapActions是同样的道理)
methods:{
...mapMutations({"方法名":"模块名/mutation名字"}),
...mapMutations({"addBook":"a/addBook"}),
}