Vue2.0 实战项目(六) Vuex

vuex

最近进入了一个新项目组,前端框架选择vue进行开发,数据的状态管理选择用vuex。本篇随笔中的代码采用vuex官网提供的购物车案例

项目结构

├── 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

State

由于Vuex的状态存储是响应式的,所以从store实例中读取状态最简单的方式是在计算属性中返回某个状态

//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辅助函数帮助我们生成计算属性

//改造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子节点名字相同,也可以传递一个字符串数组

computed: mapState([
  // 映射 this.all 为 store.state.all
  'all'
])

如果想要与局部计算属性混合使用,则可以通过对象展开运算符做到这一点

computed: {
  localComputed () { /* ... */ },
  // 使用对象展开运算符将此对象混入到外部对象中
  ...mapState({
    // ...
  })
}

Getter

Getter相当于store实例的计算属性,Getter的返回值会根据它的依赖被缓存起来,只有依赖发生改变,才会重新计算。
Getter接受State作为第一个参数,其他的getter作为第二个参数,同时也会暴露为store.getters对象

//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一样。

Mutation

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

每个 Mutation 都有一个字符串的 事件类型 (type) 和 一个回调函数(handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,通过store.commit可以传递第二个参数,也就是载荷(Payload)

// 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

Action和Mutation有两点不同:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。
actions: {
  getAllProducts ({ commit }) {
    commit('types.types.RECEIVE_PRODUCTS')
  }
}

Action通过store.dispatch方法触发
store.dispatch('getAllProducts')

在组件中分发Action,我们可以使用mapActions辅助函数将组件的methods映射为store.dispatch调用,使用方法同mapMutations

Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,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 会收到局部化的 getterdispatchcommit

原文地址:https://www.cnblogs.com/hello-wuhan/p/7750159.html