vuex狀態(tài)管理模式

對于學(xué)習過react的同學(xué)可能比較清楚呻待,在react我們是通過redux來處理狀態(tài)管理的矾削,那么現(xiàn)在火熱的vue是如何做到管理頁面數(shù)據(jù)的呢萎津,答案就是vuex咧七。

1 什么情況下使用vuex

Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式廊营。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài)歪泳,如果您不打算開發(fā)大型單頁應(yīng)用,使用 Vuex 可能是繁瑣冗余的露筒。如果您需要構(gòu)建是一個中大型單頁應(yīng)用呐伞,您很可能會考慮如何更好地在組件外部管理狀態(tài),Vuex 將會成為自然而然的選擇慎式。

下面展示一個單向數(shù)據(jù)流的圖片:


頁面中模版獲取state數(shù)據(jù)渲染頁面伶氢,用戶通過action改變數(shù)據(jù)導(dǎo)致頁面重新渲染。

2 vuex Store

每一個 Vuex 應(yīng)用的核心就是 store(倉庫)瘪吏。"store" 基本上就是一個容器癣防,它包含著你的應(yīng)用中大部分的狀態(tài)(state)。Vuex 和單純的全局對象有以下兩點不同:

Vuex 的狀態(tài)存儲是響應(yīng)式的掌眠。當 Vue 組件從 store 中讀取狀態(tài)的時候蕾盯,若 store 中的狀態(tài)發(fā)生變化,那么相應(yīng)的組件也會相應(yīng)地得到高效更新蓝丙。

你不能直接改變 store 中的狀態(tài)级遭。改變 store 中的狀態(tài)的唯一途徑就是顯式地提交(commit) mutations望拖。這樣使得我們可以方便地跟蹤每一個狀態(tài)的變化,從而讓我們能夠?qū)崿F(xiàn)一些工具幫助我們更好地了解我們的應(yīng)用挫鸽。

const store = new Vuex.Store({
  state: {
    count: 0,
    step:2
  },
  mutations: {
    increment (state) {
      state.count += state.step
    }
  }
})

現(xiàn)在说敏,你可以通過 store.state 來獲取狀態(tài)對象,以及通過 store.commit 方法觸發(fā)狀態(tài)變更:

store.commit('increment')
console.log(store.state.count) // -> 2

3vuex State

Vuex 使用 單一狀態(tài)樹 —— 是的丢郊,用一個對象就包含了全部的應(yīng)用層級狀態(tài)盔沫。至此它便作為一個『唯一數(shù)據(jù)源(SSOT)』而存在。這也意味著枫匾,每個應(yīng)用將僅僅包含一個 store 實例架诞。

// 創(chuàng)建一個 Counter 組件
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return store.state.count
    }
  }
}

我們在這個組件里面用到了count,當我們通過store.commit('increment')時婿牍,組件里會發(fā)生rerender。

計算屬性示例:

<div id="example">
  <p>my score grade: "{{ level }}"</p>
  <p>i get  "{{ score }}" point in exam</p>
  <button @click="change()">add</button>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    score: '87'
  },
  computed: {
    level: function () {
      return this.score > 60 ?  '合格' :'不合格'
    }
  },
  method:{
     change:function(){
        this.score += 5
}
}
})

我們可以看到level是依賴于score的惩歉,如果我們通過點擊button方法等脂,會同是重新計算level的值。

Vuex 通過 store 選項撑蚌,提供了一種機制將狀態(tài)從根組件『注入』到每一個子組件中(需調(diào)用 Vue.use(Vuex)):

const app = new Vue({
  el: '#app',
  // 把 store 對象提供給 “store” 選項上遥,這可以把 store 的實例注入所有的子組件
  store,
  components: { Counter },
  template: `
    <div class="app">
      <counter></counter>
    </div>
  `
})
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}

4 mapState 輔助函數(shù)

當一個組件需要獲取多個狀態(tài)時候,將這些狀態(tài)都聲明為計算屬性會有些重復(fù)和冗余争涌。為了解決這個問題粉楚,我們可以使用 mapState 輔助函數(shù)幫助我們生成計算屬性,讓你少按幾次鍵:

// 在單獨構(gòu)建的版本中輔助函數(shù)為 Vuex.mapState
import { mapState } from 'vuex'
const store = new Vuex.store({
     state:{
        apple:5,
        orange:2
}
})

export default {
    computed: mapState({
    // 箭頭函數(shù)可使代碼更簡練
    apple: state => state.apple,
    total (state) {
      return state.apple + state.orange
    }
  })
}

上面這個例子顯然易見亮垫,我們把store中的數(shù)據(jù)放到了頁面上模软。
當映射的計算屬性的名稱與 state 的子節(jié)點名稱相同時,我們也可以給 mapState 傳一個字符串數(shù)組饮潦。
computed: mapState([
// 映射 this.count 為 store.state.count
'apple燃异,orange'
])

有了對象擴展符,我們可以這樣寫:

computed: {
     attribute1:function(){
    },
     ...mapState([apple,orange])
}

5 vuex Getter

Vuex 允許我們在 store 中定義『getters』(可以認為是 store 的計算屬性)继蜡。Getters 接受 state 作為其第一個參數(shù):

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

Getters 會暴露為 store.getters 對象:

store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]

Getters 也可以接受其他 getters 作為第二個參數(shù):

getters: {
  // ...
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}
store.getters.doneTodosCount // -> 1

我們可以很容易地在任何組件中使用它:

computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}

getters的神奇之處就是參數(shù)可以有2個回俐,state和getters,所以這就有無限可能稀并。

6 mapGetters 輔助函數(shù)

mapGetters 輔助函數(shù)僅僅是將 store 中的 getters 映射到局部計算屬性:

import { mapGetters } from 'vuex'
const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false },
      { id: 3, text: '...', done: true },
   ],
   another:2
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    },
   doneCount:(state,getter){
      return state.another + getters.doneCounts.length
    }
  }
})

export default {
  computed: {
    ...mapGetters([
      'doneTodosCount',
      'doneCount',
      // ...
    ])
  }
}

如果你想將一個 getter 屬性另取一個名字仅颇,使用對象形式:

mapGetters({
  // 映射 this.doneCount 為 store.getters.doneTodosCount
  doneCount: 'doneTodosCount'
})

7 Mutation

更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。Vuex 中的 mutations 非常類似于事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調(diào)函數(shù) (handler)碘举。這個回調(diào)函數(shù)就是我們實際進行狀態(tài)更改的地方忘瓦,并且它會接受 state 作為第一個參數(shù):

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state,n) {
      // 變更狀態(tài)
      state.count += n
    }
  }
})

store.commit('increment', 10)

其實我還可以傳對象。

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
store.commit('increment', {
  amount: 10
})

對象風格的提交方式

store.commit({
  type: 'increment',
  amount: 10
})

當使用對象風格的提交方式引颈,整個對象都作為載荷傳給 mutation 函數(shù)政冻,因此 handler 保持不變

接下來知道我要講什么嘛枚抵,沒錯就是map了。明场。汽摹。

import { mapMutations } from 'vuex'

computed: {
    ...mapGetters([
      'doneTodosCount',
      'count',
    ])
},
methods: {
     ...mapMutations([
        'increment','decrement',
    ]),
     change(){
         this.increment()   == this.store.commit('increment')
         this.doneTodosCount += this.count       
   }

注意執(zhí)行了increment后count的值發(fā)生了變化。

8 vuex Action

Action 類似于 mutation苦锨,不同在于:

  • Action 提交的是 mutation逼泣,而不是直接變更狀態(tài)。
  • Action 可以包含任意異步操作舟舒。

讓我們來注冊一個簡單的 action:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment ({commit}) {
        commit('increment')
    }
  }
})

乍一眼看上去感覺多此一舉拉庶,我們直接分發(fā) mutation 豈不更方便?實際上并非如此秃励,還記得 mutation 必須同步執(zhí)行這個限制么氏仗?Action 就不受約束!我們可以在 action 內(nèi)部執(zhí)行異步操作:

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

通過store.dispatch('increment')分發(fā)夺鲜。舉個例子說吧


import * as types from './mutation-types'

const store = new vex.store({
  mutation:{
     addcart(state, product){
         state.count += product.num
   }
 },
  actions: {
  addToCart ({ commit, state }, product) => {
  if (product.inventory > 0) {
    commit(types.ADD_TO_CART, {
       num: product.num
    })
  }
}

可以用store.dispatch('addToCart',{num:3,name:'fruit'})來分發(fā)皆尔。
當然這個也有map用法,和前面講的一樣币励。

組合 Actions

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}

store.dispatch('actionA').then(() => {
  // ...
})

對promise不夠了解的可以去學(xué)一下慷蠕。

在另外一個 action 中也可以:

actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}

好了,今天講的東西有點多食呻,下回我會結(jié)合實際項目詳解vuex的用法流炕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市仅胞,隨后出現(xiàn)的幾起案子每辟,更是在濱河造成了極大的恐慌,老刑警劉巖干旧,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件影兽,死亡現(xiàn)場離奇詭異,居然都是意外死亡莱革,警方通過查閱死者的電腦和手機峻堰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盅视,“玉大人捐名,你說我怎么就攤上這事∧只鳎” “怎么了镶蹋?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我贺归,道長淆两,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任拂酣,我火速辦了婚禮秋冰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘婶熬。我一直安慰自己剑勾,他們只是感情好,可當我...
    茶點故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布赵颅。 她就那樣靜靜地躺著虽另,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饺谬。 梳的紋絲不亂的頭發(fā)上捂刺,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天,我揣著相機與錄音募寨,去河邊找鬼族展。 笑死,一個胖子當著我的面吹牛绪商,可吹牛的內(nèi)容都是我干的苛谷。 我是一名探鬼主播辅鲸,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼格郁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了独悴?” 一聲冷哼從身側(cè)響起例书,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刻炒,沒想到半個月后决采,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡坟奥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年树瞭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爱谁。...
    茶點故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡晒喷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出访敌,到底是詐尸還是另有隱情凉敲,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站爷抓,受9級特大地震影響势决,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蓝撇,卻給世界環(huán)境...
    茶點故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一果复、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧唉地,春花似錦据悔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至群嗤,卻和暖如春菠隆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背狂秘。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工骇径, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人者春。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓破衔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親钱烟。 傳聞我的和親對象是個殘疾皇子晰筛,可洞房花燭夜當晚...
    茶點故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容