vuex详解

一、vuex的基本使用

1、安装

npm install vuex --save-dev

2、在Vue项目的src文件夹下建一个名为store的文件夹,该文件夹下建一个index.js文件

src/store/index.js

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
 
// Vuex 的状态存储是响应式的
 
// 创建一个Vuex.Store的实例
const store = new Vuex.Store({
 
  // 存储状态
  state: {},
 
  // 派生状态 类似Vue的计算属性
  getters: {},
 
  // 变更状态,更改 store 中状态的唯一方法是提交 mutation
  mutations: {},
 
  // 异步变更状态,Action 提交的也是 mutation
  actions: {}
});
 
export default store;

3、在Vue项目的main.js文件中导入store,并把store注册到Vue实例上,就可以将store从根组件“注入”到每一个子组件中,这样任意子组件能通过 this.$store访问到。

import Vue from 'vue';
import App from './App';
// 导入store
import store from './store';
import router from './router';
 
Vue.config.productionTip = false;
 
new Vue({
  el: '#app',
  router,
  store, // store注册到Vue实例上
  components: { App },
  template: '<App/>'
});

二、State 

1、state中存储状态

  state: {
    count: 0,
    username: 'liu',
    password: 123
  }

2、在Vue组件中使用store中存储的状态

<template>
  <div>
    <!-- 声明为计算属性后使用 -->
    <h1>{{ count }}</h1>
    <h1>{{ username }}</h1>
 
    <!-- 直接利用$store使用  -->
    <h2>{{ $store.state.count }}</h2>
    <h2>{{ $store.state.username }}</h2>
  </div>
</template>
 
<script>
export default {
  data () {
    return {};
  },
  computed: {
    // store中的状态都声明为计算属性
    count () {
      return this.$store.state.count;
    },
    username () {
      return this.$store.state.username;
    }
  },
  mounted () {
    // 声明为计算属性后使用
    console.log(this.count);
    console.log(this.username);
    // 子组件能通过this.$store访问到
    console.log(this.$store);
    console.log(this.$store.state.count);
    console.log(this.$store.state.username);
  }
};
</script>

$store是挂载在 Vue 实例上的(即Vue.prototype),而组件也其实是一个Vue实例,在组件中可使用this访问原型上的属性,template 拥有组件实例的上下文,可直接通过{{ $store.state.count }}访问,等价于 script 中的this.$store.state.count。

由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态,因此在项目中使用都是先声明为计算属性,再使用

3、mapState辅助函数

当一个组件需要获取多个状态时,将这些状态都声明为计算属性会有些重复和冗余。因此可以利用mapState辅助函数

<template>
  <div>
    <!-- 声明为计算属性后使用 -->
    <h1>{{ count }}</h1>
    <h1>{{ username }}</h1>
    <h1>{{ password }}</h1>
  </div>
</template>
 
<script>
// 引入mapState函数
import { mapState } from 'vuex';
 
export default {
  data () {
    return {};
  },
 
  // 利用mapState函数将store中的状态都声明为计算属性
  computed: mapState({
    count (state) {
      return state.count;
    },
    // 箭头函数使代码更简练
    username: state => state.username,
    // 传字符串参数 'password' 等同于 `state => state.password`
    password: 'password'
  }),
 
  mounted () {
    // 声明为计算属性后使用
    console.log(this.count);
    console.log(this.username);
    console.log(this.password);
  }
};
</script>

上面我们声明的计算属性的名称与 state 的子节点名称相同,因此可以把声明计算属性简化为给 mapState传一个字符串数组。

// 引入mapState函数
import { mapState } from 'vuex';
 
export default {
  data () {
    return {};
  },
 
  // 利用mapState函数将store中的状态都声明为计算属性
  computed: mapState(['count', 'username', 'password']),
 
  mounted () {
    // 声明为计算属性后使用
    console.log(this.count);
    console.log(this.username);
    console.log(this.password);
  }
};

4、展开运算符+mapState辅助函数(...mapState) 声明计算属性

// 引入mapState函数
import { mapState } from 'vuex';
 
export default {
  data () {
    return {};
  },
  // 利用...mapState将store中的状态都声明为计算属性
  computed: {
    ...mapState(['count', 'username', 'password'])
  },
  mounted () {
    // 声明为计算属性后使用
    console.log(this.count);
    console.log(this.username);
    console.log(this.password);
  }
};

三、Getter 

1、派生状态

// 创建一个Vuex.Store的实例
const store = new Vuex.Store({
 
  state: {
    count: 0,
    username: 'liu'
  },
 
  // 派生状态
  // Getter 接受 state 作为其第一个参数
  getters: {
    message (state) {
      return '当前最新的count值是:' + state.count;
    },
    // 箭头函数
    msg: state => {
      return `用户名为:${state.username}`;
    }
  }
});

2、在组件中使用getters(this.$store.getters.***)

<template>
  <div>
    <p>{{$store.getters.message}}</p>
    <p>{{$store.getters.msg}}</p>
  </div>
</template>
 
<script>
export default {
  data () {
    return {};
  },
 
  mounted () {
    console.log(this.$store.getters);
    console.log(this.$store.getters.message);
    console.log(this.$store.getters.msg);
  }
};
</script>

3、展开运算符+mapGetters辅助函数(...mapGetters)

mapGetters辅助函数仅仅是将 store 中的 getter 映射到局部计算属性

<template>
  <div>
    <p>{{message}}</p>
    <p>{{msg}}</p>
  </div>
</template>
 
<script>
// 导入mapGetters函数
import { mapGetters } from 'vuex';
 
export default {
  data () {
    return {};
  },
 
  computed: {
    ...mapGetters(['message', 'msg'])
  },
 
  mounted () {
    console.log(this.message);
    console.log(this.msg);
  }
};
</script>

注意:

getters只负责对外提供数据,不负责修改数据,如果想要修改state中的数据,通过提交mutation

getters中的方法,和组件中的过滤器比较类似,因为过滤器和getters都没有修改原数据,都是对原数据做一层包装,然后提供给调用者

getters和computed比较像,如果gettres引用了state中的某个数据,只要state中的这个数据发生变化,就会立即触发getters的重新求值

四、Mutation 

1、变更状态

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。

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

// 创建一个Vuex.Store的实例
const store = new Vuex.Store({
 
  // 存储状态
  state: {
    count: 0
  },
 
  // mutation中最多有两个参数,第一个参数为state,要传多个参数,可传递一个对象
  mutations: {
    // 类型为 increment 的 mutation
    increment (state) {
      // 变更状态
      state.count++;
    },
    // 类型为 reduce 的 mutation
    reduce (state, n) {
      // 变更状态
      state.count -= n;
    },
    // 类型为 change 的 mutation,第二个参数为一个对象
    change (state, payload) {
      // 变更状态
      state.count = state.count + payload.a + payload.b;
    }
  }
});

2、在Vue组件中提交 mutation

以相应的 type 调用 store.commit 方法

<template>
  <div class="hello">
    <button @click="add">加1</button>
    <button @click="reduce">减2</button>
    <button @click="change">change</button>
 
    <h1>{{ count }}</h1>
  </div>
</template>
 
<script>
import { mapState } from 'vuex';
 
export default {
  data () {
    return {};
  },
  computed: {
    ...mapState(['count'])
  },
 
  methods: {
    add () {
      // 以相应的 type 调用 store.commit 方法
      this.$store.commit('increment');
    },
    reduce () {
      // 可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload)
      this.$store.commit('reduce', 2);
    },
    change () {
       // 注意:只可以向 store.commit 传入额外的一个参数
      // 如果要传多个参数,就传一个对象作为参数,即大多数情况下,载荷应是一个对象
      this.$store.commit('change', {a: 4, b: 6});
    }
  }
 
};
</script>

3、对象风格提交mutation

提交 mutation 的另一种方式是直接使用包含 type 属性的对象

当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数

<template>
  <div class="hello">
    <button @click="add">加1</button>
    <button @click="reduce">减2</button>
    <button @click="change">change</button>
 
    <h1>{{ count }}</h1>
  </div>
</template>
 
<script>
import { mapState } from 'vuex';
 
export default {
  data () {
    return {};
  },
  computed: {
    ...mapState(['count'])
  },
 
  methods: {
    add () {
      // 以相应的 type 调用 store.commit 方法
      this.$store.commit({
        type: 'increment'
      });
    },
    reduce () {
      // 这种没法改用对象风格提交mutation
      // 因为第二个参数必须是对象才能使用对象风格提交mutation
      this.$store.commit('reduce', 2);
    },
    change () {
      this.$store.commit({
        type: 'change',
        a: 4,
        b: 6
      });
    }
  }
 
};
</script>

4、展开运算符+mapMutations辅助函数(...mapMutations)

<template>
  <div class="hello">
    <button @click="increment">加1</button>
    <button @click="reduce(2)">减2</button>
    <button @click="change({a:4, b:6})">change</button>
 
    <h1>{{ count }}</h1>
  </div>
</template>
 
<script>
// 导入mapState和mapMutations函数
import { mapState, mapMutations } from 'vuex';
 
export default {
  data () {
    return {};
  },
  computed: {
    ...mapState(['count'])
  },
 
  methods: {
    // 展开运算符+mapMutations辅助函数
    ...mapMutations(['increment', 'reduce', 'change'])
  }
};
</script>

五、Action 

1、异步变更状态

// 创建一个Vuex.Store的实例
const store = new Vuex.Store({
 
  state: {
    count: 0
  },
 
  mutations: {
    increment (state) {
      state.count++;
    }
  },
 
  // 异步变更状态,Action 提交的也是 mutation
  // Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象
  // Action调用 context.commit 提交一个 mutation
  // Action通过 context.state 和 context.getters 获取 state 和 getters。
  actions: {
    increment (context) {
      context.commit('increment');
    }
  }
});

2、在组件中使用actions

在组件中使用actions和在组建中使用mutations类似

区别就是,mutations使用this.$store.commit('mutation方法名')提交mutation;actions使用this.$store.dispatch('action方法名')分发action

<template>
  <div>
    <button @click="increment">加1</button>
 
    <h1>{{ count }}</h1>
 
  </div>
</template>
 
<script>
 
export default {
  data () {
    return {};
  },
 
  methods: {
    increment () {
      return this.$store.dispatch('increment');
    }
  }
};
</script>

3、展开运算符+mapActions 辅助函数(...mapActions )

<template>
  <div>
    <button @click="increment">加1</button>
 
    <h1>{{ count }}</h1>
 
  </div>
</template>
 
<script>
// 导入mapActions函数
import { mapActions } from 'vuex';
 
export default {
  data () {
    return {};
  },
 
  methods: {
    // 展开运算符+mapActions辅助函数
    ...mapActions(['increment'])
  }
};
</script>

六、Module

Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块,用于解决复杂应用的状态过多,管理复杂的问题

(1)不带命名空间的模块

默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的

模块的状态默认注册在全局命名空间,为一个对象,对象中的属性是模块中的状态

模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。

对于模块内部的 getter,根节点状态作为第三个参数(rootState)暴露出来

在模块的muatations中不能使用根节点状态,因为muatations中是同步更新状态

对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState

(2)带命名空间的模块

如果希望模块具有更高的封装度和复用性,可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。

当带命名空间的模块被注册后,它所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。

getters中使用全局 state 和 getter,rootState 和 rootGetters 会作为第三和第四参数传入 getter

全局 state会通过 context 对象的属性(context.rootState)传入 action

七、总结

1、state中的数据,不能直接修改,如果想要修改,必须通过mutations

2、如果组件想要直接从state上获取数据:需要this.$store.state.***

3、如果组件想要修改数据,必须使用mutations提供的方法,需要通过this.$store.commit('方法的名称', 唯一的一个参数)

4、如果store中state上的数据,对外提供的时候需要做一层包装,推荐使用getters

5、如果使用getters,则在组建中获取数据,使用this.$store.getters.***

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值