vue-demo-collection已升級爲2.x版本,
本文是基於vue全家桶1.x和webpack1.x寫的demo,
查看2.x源碼移步shopping-cart2.x,
查看1.x和2.x代碼對比移步shopping-cart源碼比較。javascript
本文是上篇文章的序章,一直想有機會再次實踐下Vuex。寫下這篇總結,See Demo。html
Flux-inspired Application Architecture for Vue.jsvue
Vuex其實是類Flux的數據管理架構。它主要幫咱們更好的組織代碼,更好的讓Vue中的狀態更好的經過狀態管理維護起來。在實際項目運用中咱們須要對組件的 組件本地狀態(component local state)
和 應用層級狀態(application level state)
進行區分。
Vuex的做用就是聚集應用層級的狀態到一處,方便管理。java
說狀態其實有些同窗可能不太理解,那麼在剛上手Vue的時候作過的例子中,一個Vue實例中都會有data,那麼這個data中保存的屬性其實就是能夠理解爲狀態。webpack
試想這樣的場景,好比一個Vue根實例下面有一個根組件名爲App.vue
,它下面有兩個子組件A.vue
和B.vue
,父組件和子組件之間使用Props通信是沒問題的,經過綁定Props輕鬆的實現。git
可是若是咱們須要A.vue組件和B.vue組件之間通信呢?由於組件實例的做用域是孤立的,它們之間是不能直接通信的。那麼只能藉助共有的父組件經過自定義事件實現。github
A組件想要和B組件通信每每是這樣的:web
A小弟說:「報告老大,可否幫我捎個信給你的小弟B組件?」,它須要dispatch
一個事件給Appvuex
App老大說:「包在我身上」,它須要監聽A組件dispatch
的事件,同時須要broadcast
一個事件給B組件。架構
B小弟說:「信息已收到。」,它須要on
監聽App組件分發的事件。
這只是一條通信路徑,那麼若是父組件下有多個子組件,子組件之間通信的路徑就會變的很繁瑣,父組件須要監聽大量的事件,還須要負責分發給不一樣的子組件。很顯然這並非咱們想要的組件化開發體驗。
Vuex就是爲了解決這一問題出現的。
下面這張圖很好的詮釋了Vuex和組件之間的通信關係。
這張圖描述的很棒,完整的數據流閉環,整個應用的數據流是單向的。對咱們理解Vuex和Vue的組件間的通信關係頗有幫助。
須要掌握的:
用戶在組件中的輸入操做觸發 action 調用;
Actions 經過分發 mutations 來修改 store 實例的狀態;
Store 實例的狀態變化反過來又經過 getters 被組件獲知。
進入正題。一個購物車咱們都須要完成哪些功能?先看看Demo的樣子。
顯示商品的文字描述、圖片描述、類型、價格信息;
改變商品顏色的時候圖片切換;
改變商品類型的時候價格變化;
加入/移除購物車;
購物車中商品數量統計以及總價的統計;
我參考了中型到大型項目的目錄結構說明構建的購物車,把Vuex相關的代碼分割到多個模塊(module),我認爲這樣更清晰明瞭。
每個 Vuex 應用的核心就是 store(倉庫)。"store" 基本上就是一個容器,它包含着你應用裏大部分的 狀態(即 state),咱們建立store.js
導入各個模塊的初始狀態和 mutations。
store.js
// vuex/store.js import Vue from 'vue' import Vuex from 'vuex' import index from './modules/index' Vue.use(Vuex) export default new Vuex.Store({ // 組合各個模塊 modules: { index } })
經過在根實例中註冊 store 選項,該 store 實例會注入到根組件下的全部子組件中。
App.vue
import Nav from './components/Nav.vue' import store from './vuex/store' export default { name: 'App', store, data() { return { // note: changing this line won't causes changes // with hot-reload because the reloaded component // preserves its current state and we are modifying // its initial state. } }, components: { 'cart-nav': Nav } }
配合ES6的箭頭函數真的很簡潔的。
vuex: { getters: { iPhone6S: ({ index }) => index.iPhone6S } }
好比咱們須要根據更改外觀來改變商品的圖片這樣的需求。咱們該怎樣完成一個數據流閉環呢?
從組件開始:
一(Vue components
)、首先子組件Index.vue
須要一個點擊事件來觸發action:
Index.vue
<li v-for="styleUrl in iPhone6S.style" @click="changeStyle($key, styleUrl)" :class="{active: iPhone6S.activeStyleUrl == styleUrl}"><span v-text="$key"></span></li>
二(Actions
)、聲明一個名爲changeStyle
的action
actions.js
export const changeStyle = makeAction('CHANGE_STYLE')
並分發mutations,用統一的函數處理。
function makeAction (type) { return ({ dispatch }, ...args) => dispatch(type, ...args) }
三(Mutations
)、咱們用常量聲明mutation,並把它放到單獨的地方。mutation常量習慣性大寫的,區分於actions。
mutation-types.js
export const CHANGE_STYLE = 'CHANGE_STYLE'
四(State
)、在模塊中導入mutation改變狀態:
index.js
[CHANGE_STYLE] (state, styleName, styleUrl) { state.iPhone6S.activeStyle = styleName state.iPhone6S.activeStyleUrl = styleUrl }
因爲 Vuex store 內部的 state 對象被 Vue 改形成了響應式對象,當咱們對 state 進行修改時,任何觀測着 state 的 Vue 組件都會自動地進行相應地更新。
使用Vuex管理狀態並非須要把全部的狀態都放在Vuex裏。如上所述,組件本地狀態是不須要寫在Vuex裏的。
在使用vuex的過程當中你有可能會對從vuex.getters獲取的數據進行再次操做。這是不容許的。改變 store 中的狀態的惟一途徑就是顯式地分發 狀態變動事件
組件永遠都不該該直接改變 Vuex store 的狀態。由於咱們想要讓狀態的每次改變都很明確且可追蹤,Vuex 狀態的全部改變都必須在 store 的 mutation handler (變動句柄)中管理。
使用Vue Tools調試vuex是一個很是愉快的體驗,能夠在Components
中清楚的看到哪些數據是從vuex.getters中獲取來的。
它記錄了每次mutation的狀態變化,保存了狀態變化後的快照,咱們能夠定位到你想檢查的快照觀察數據的變化。