最近进入了一个新项目组,前端框架选择vue进行开发,数据的状态管理选择用vuex。本篇随笔中的代码采用vuex官网提供的购物车案例。html
├── index.html ├── main.js ├── api │ └── shop.js # 抽取出API请求 ├── components │ ├── App.vue # 根级别的页面 │ ├── Cart.vue # 购物车组件 │ └── ProductList.vue # 产品组件 │ └── store ├── index.js # 咱们组装模块并导出 store 的地方 ├── actions.js # 根级别的 action ├── mutations.js # 根级别的 mutation ├── mutation-types.js # mutation事件类型 └── modules ├── cart.js # 购物车模块 └── products.js # 产品模块
Vuex有五个核心概念,分别是:State、Getter、Mutation、Action和Module前端
因为Vuex的状态存储是响应式的,因此从store实例中读取状态最简单的方式是在计算属性中返回某个状态vue
//product.js const state = { all: [{ 'id': 1, 'title': 'iPad 4 Mini', 'price': 500.01 },{ 'id': 2, 'title': 'H&M T-Shirt White', 'price': 10.99 }] } export default { state }
//productList.vue import store from '../store/index' <template> <ul> <li v-for="p in products"> {{ p.title }} - {{ p.price | currency }} </li> </ul> </template> <script> export default { computed: { products() { return store.state.all } }) } </script>
当一个组件须要获取多个状态时,能够经过mapState
辅助函数帮助咱们生成计算属性git
//改造productList.vue import { mapState } from 'vuex' export default { computed: mapState({ // 箭头函数可以使代码更简练 products: state => state.all, // 传字符串参数 'all' 等同于 `state => state.all` //products: 'all', // 为了可以使用 `this` 获取局部状态,必须使用常规函数 /* products (state) { return state.all + this.localCount } */ }) }
若是计算属性名和state子节点名字相同,也能够传递一个字符串数组github
computed: mapState([ // 映射 this.all 为 store.state.all 'all' ])
若是想要与局部计算属性混合使用,则能够经过对象展开运算符作到这一点vuex
computed: { localComputed () { /* ... */ }, // 使用对象展开运算符将此对象混入到外部对象中 ...mapState({ // ... }) }
Getter至关于store实例的计算属性,Getter的返回值会根据它的依赖被缓存起来,只有依赖发生改变,才会从新计算。
Getter接受State做为第一个参数,其余的getter做为第二个参数,同时也会暴露为store.getters对象api
//products.js const getters = { allProducts: (state, getter) => state.all } export default { state, getters }
//productList.vue computed: { allProducts() { return this.$store.getters.allProducts } }
一样,Getter也有辅助函数mapGetters
,它的做用是将store中的getter映射到局部计算属性,使用方法与mapState
同样。数组
更改 Vuex 的 store 中的状态的惟一方法是提交 Mutation。缓存
每一个 Mutation 都有一个字符串的 事件类型 (type) 和 一个回调函数(handler)。这个回调函数就是咱们实际进行状态更改的地方,而且它会接受 state 做为第一个参数,经过store.commit能够传递第二个参数,也就是载荷(Payload)bash
// project.js // 可使用常量代替Mutation事件类型 const mutations = { [types.RECEIVE_PRODUCTS] (state, { products }) { state.all = products }, [types.ADD_TO_CART] (state, { id }) { state.all.find(p => p.id === id).inventory-- } } // actions const actions = { getAllProducts ({ commit }) { shop.getProducts(products => { commit(types.RECEIVE_PRODUCTS, { products }) }) } }
// mutation-types.js export const ADD_TO_CART = 'ADD_TO_CART' export const RECEIVE_PRODUCTS = 'RECEIVE_PRODUCTS'
Mutation也有辅助函数mapMutations
import { mapMutations } from 'vuex' export default { methods: { ...mapMutations([ 'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')` // `mapMutations` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')` }) } }
Mutation必须是同步函数,若是想包含异步操做,那么必需要使用Action
Action和Mutation有两点不一样:
actions: { getAllProducts ({ commit }) { commit('types.types.RECEIVE_PRODUCTS') } }
Action经过store.dispatch
方法触发
store.dispatch('getAllProducts')
在组件中分发Action,咱们可使用mapActions
辅助函数将组件的methods映射为store.dispatch
调用,使用方法同mapMutations
。
因为使用单一状态树,应用的全部状态会集中到一个比较大的对象。当应用变得很是复杂时,store 对象就有可能变得至关臃肿。
为了解决以上问题,Vuex 容许咱们将 store 分割成模块(module)。每一个模块拥有本身的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行一样方式的分割:
const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } } const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } }) store.state.a // -> moduleA 的状态 store.state.b // -> moduleB 的状态
默认状况下,模块内部的action、mutation、getter是注册在全局命名空间的,若是须要模块被更好的封装,那么能够经过添加namespaced: true
的方式使其成为命名空间模块
启用了命名空间的 getter 和 action 会收到局部化的 getter
,dispatch
和 commit
。