Vue2.0 實戰項目(六) Vuex

vuex

最近進入了一個新項目組,前端框架選擇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前端

State

因爲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

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同樣。數組

Mutation

更改 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

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

相關文章
相關標籤/搜索