VUE組件系統極速入門2--vuex

看完了官網vuex一頭霧水的,比如vuex中的...mapState、countAlias: 'count'等。沒看過官網也沒事,希望對你們有幫助。
下文都是我的個人理解,官網的就引用了代碼,也許存在著很多錯誤的描述或者理解,請各位請教,我會改正的。

這個坑之前就深挖了~現在是時候填了。
為什么要有vuex?
它是管理組件與組件通信的庫。
子組件與父組件之間通信還是很方便的。但是如果是同級組件,或者是其它的復雜情況,比如:

  • a.vue中的子組件想與b.vue中的子組件的通迅
  • a.vue想與b.vue中的子組件的通迅
  • a.vue中的子組件想與b.vue通迅
  • a.vue的子組件的子組件想與b.vue的子組件通迅
  • .....

就是為了解決了類似的問題~
我們把事件想簡單點吧,如果你管理那么多組件(這個改變了那個的數據,那個改變了這個的數據,那個組件又多了點什么,這個又少了點什么),你會怎么做呢?就說想法,不用實現。
我的想法是:沒有蛀牙 把整個應用的數據全部集中起來,誰想用作顯示都無條件同意,而誰想改,改完了一定要通知所以用到這個數據顯示的組件,還不是美滋滋?
vuex也是這個意思,它一共分為以下5個部分

  • state 放數據的
  • Getter 獲取數據的
  • Mutation 同步改變數據的
  • Action 異步改變數據(利用Mutation實現)
  • Module 可以把全局的數據倉庫分為幾個單獨的模塊,比如用戶一個,文章一個。

1 state

1.1 state 定義

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

然后mutations先提一下怎么用吧(官網的例子,原汁原味 )

store.commit('increment')

console.log(store.state.count) // -> 1

然后要把這貨放在組件里,如果放入父組件中,那么子組件也可以使用。通過 this.$store。

//普通的定義
const app = new Vue({
  el: '#app',
  // 把 store 對象提供給 “store” 選項,這可以把 store 的實例注入所有的子組件
  store,
  components: {XXX},
  template: `XXX`
})
//*.vue
export default {
  el: '#app',
  store,
  components: {XXX}
}
</script>

1.2 關于mapState

mapState的作用說到底,就是兩個作用,先說第一個:別名。
上官網的例子,真的是要了親命了唉):

// 在單獨構建的版本中輔助函數為 Vuex.mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // 箭頭函數可使代碼更簡練
    count: state => state.count,

    // 傳字符串參數 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 為了能夠使用 `this` 獲取局部狀態,必須使用常規函數
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

先說說mapState的作用,返回一個對象。
什么樣的對象

{    
    cout: xxx //第一個
    coutAlias:xxx //第二個
    countPlusLocalState(state){xxxx}//第三個
}

我盡可能說詳細一點,

export default {
  computed: mapState({
   /*根據箭頭函數,生成的函數為
   function(state){ return state.count},
   所以count的值最后是state.count。count是個值。*/
    count: state => state.count,
    /* countAlias只是個名字,與上面的count沒有一毛錢關系,我們的大前提,
是使用的vuex里的mapstate,所以,只要我們給一個store里的屬性名,
mapstate就會把屬性名對應的屬性返回給我們,
并用我們提供的新名字。所以,countAliae就對應的state里的count。
   */
    countAlias: 'count',
/*注意,我強調一下,如果你只給屬性名,不給別名,那么就會按原屬性名返回。  比如就是一個孤零零的      'a'    你調用的時候也要調a就完事了,上面的是調countAlias  */

    // 為了能夠使用 `this` 獲取局部狀態,必須使用常規函數
    /*  箭頭函數基礎,親,如果你看不懂為什么要這樣,請去參考下ES6的箭頭函數 */
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

接下來講mapState的第二個作用:偷懶。
突然出現了一個...mapState,這個...就是對象展開運算符,是ES6里的,可關鍵是為什么搞了這么一個另人費解的東西,有什么用呢?
答:并沒有什么卵用還是有點用的,比如,你的compute屬性里想用store里返回的東西,一共用5個!就可以用mapstate傳5個屬性名進去把五個搞出來再添加到compute里。
比如

computed:{
                    a1:function(){xxxxx}
}

可是你需要a2到a6,如果一個一個的寫的話,就會這樣:

  computed:{
                    a1:function(){xxxxx},
                    a2:function(){return this.$store.a2},
                    a3:function(){return this.$store.a3},
                    .......
}

有了這個...

//注意,我再強調一下,如果你只給屬性名,不給別名,
那么就會按原屬性名返回。所以可以這么寫:
compute:{
          a1:function(){xxxxx},
        ...mapState({'a2','a3','a4','a5','a6'})
}

然后就全有了,和上面的作用一樣,看吧,偷懶就完事了。

2 Getter

首先要審明下,做為讀書人的我,也是會罵人的!?。?!
之前很多程序員總是有些很奇怪的愛好,比如他母親的Getter。這個Getter一見到名字就是想起了被java支配的恐懼,所以不說了 所以一定要好好理解!
vuex里的getter,是在store里的一個東西,為什么需要它呢?比如,
this.$store.state.todos.filter(todo => todo.done).length這一句分別在十個函數里都出現了,就可以把他考慮做成一個getter函數。
把官網的例子搞了過來

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)
    }
  }
})

Getter 會暴露為 store.getters 對象:

store.getters.doneTodos // 運行會得到 [{ id: 1, text: '...', done: true }]

Getter接受其他 getter 作為第二個參數,很奇怪是吧,但是有時候需要用到getters里的別的函數。(這里定義時不支持this直接拿)

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

在任何組件中(只要是上一級組件有store的)都可以使用:

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

getter 為什么好好的要返回一個函數?因為你返回了一個函數,還可以給函數傳值,讓他查些什么玩意,難道你們沒有發現上面的例子都只是沒有傳我們自定義的參數的?現在發現了就行~

getters: {
  // ...
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

還有剩下的mapGetters,嘿嘿,和上面的mapState一樣,返回屬性的規則也一樣:1 別名 2 偷懶

3 Mutation

如果 1與2都是獲取數據展示的話,那么3與4就是修改完通知了~
搞一手官網:

更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似于事件:每個 mutation 都有一個字符串的事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是我們實際進行狀態更改的地方,并且它會接受 state 作為第一個參數。
一個最基本的例子:

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 變更狀態
      state.count++
    }
  }
})
//使用:
store.commit('increment')
//注意,這里的increment 只能通過store.commit('increment')才能觸發

載荷,傳事件的時候同時傳個參數

既然能傳一個字符串的事件類型的increment,那么一定會有別的更復雜的使用方法。妥妥的,commit里還能搞個別的東西傳過去,這個就叫:載荷(payload)(我覺得可以理解成: 裝備)

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
//提交的風格有兩種(還是從官網上搞的)
//風格1:
store.commit('increment', {
  amount: 10
})
//風格2:(個人覺得這個逼格更高一點點)
store.commit({
  type: 'increment',
  amount: 10
})

使用payload也是有條件的,

  • 提前在你的 store 中初始化好所有所需屬性
  • 在對象上添加新屬性時,應該更新這個屬性,而不是再用一個新的對象
    • 使用 Vue.set(obj, 'newProp', 123)
    • 以新對象替換老對象。state.obj = { ...state.obj, newProp: 123 }

使用更短的名字使用Mutation&在組件中使用Mutation

你可以在組件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 輔助函數將組件中的 methods 映射為 store.commit 調用(需要在根節點注入 store)。 (官網原話)

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')`*/
    })
  }
}

然后官網也提到了使用mutations的注意事項
不能使用異步函數(等待時間不能太長的函數,什么網絡連接,讀寫文件,都不是異步函數)

如果你的程序中,有很多'increment'這種事件,推薦用常量保存后放在一個文件中,就像這樣:

//文件名: mutation-types.js
export const GO_TO_PICS = 'GO_TO_PICS'
export const GO_TO_NEWS = 'GO_TO_NEWS'
export const GO_TO_WE = 'GO_TO_WE'

4 Action

離4這個標題不遠處,你依稀可以看到,mutations不能使用異步函數,
Action就是提交異步事件的~
Action 提交的是 mutation,Action提交的是Action ,提交的是 mutation。重要的事件要說三遍。所以,Action是在mutation外又加了一層,看一個簡單的例子。

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

context是什么玩意?有什么用?
答:他是和 store 實例具有相同方法和屬性的 context 對象。作用是[個人猜測,未驗證]context對象作為store的輔助,也許是store的一些操作一定要通過context進行。

調用 context.commit 提交一個 mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters。

少寫一點(引用自網站,純語法):

實踐中,我們會經常用到 ES2015 的 參數解構 來簡化代碼(特別是我們需要調用 commit 很多次的時候):

actions: {
//這是ES6的語法
/*舉個例子:{ blowUp } = { blowUp: 10 }; 結果:{blowUp:10}
從blowUp中取一個叫blowUp的屬性。
參數本來是傳過來的context,從context中取一個commit的東西,得到  { commit : context.commit };
挺另人頭大的。
*/
  increment ({ commit }) {
    commit('increment')
  }
}

使用action
store.dispatch('increment')
但是最主要是異步調用:

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

同樣的支持載荷(payload,裝備)

// 以載荷形式分發
store.dispatch('incrementAsync', {
  amount: 10
})

// 以對象形式分發
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

官網上的一個更復雜的例子-購物車:

actions: {
// es6的語法,從context中取commit與state
  checkout ({ commit, state }, products) {
    // 把當前購物車的物品備份起來 ...對象展開
    const savedCartItems = [...state.cart.added]
    // 發出結賬請求,然后樂觀地清空購物車
    commit(types.CHECKOUT_REQUEST)
    // 購物 API 接受一個成功回調和一個失敗回調
    shop.buyProducts(
      products,
      // 成功操作
      () => commit(types.CHECKOUT_SUCCESS),
      // 失敗操作
      () => commit(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}

上面只是定義,還有在組件中使用action的方法,不要忘記這個。
關于mapActions,作用和定義你們懂得,下面用的官網的例子:

你在組件中使用 this.$store.dispatch('xxx') 分發 action,或者使用 mapActions 輔助函數將組件的 methods 映射為 store.dispatch 調用(需要先在根節點注入 store):

import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment', /* 將 `this.increment()` 映射為
 `this.$store.dispatch('increment')`*/

      // `mapActions` 也支持載荷:
      'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.dispatch('incrementBy', amount)`
    ]),
    ...mapActions({
      add: 'increment' // 將 `this.add()` 映射為 `this.$store.dispatch('increment')`
    })
  }
}

組合 Action(全盤搬的官網,這個部分很好理解)

Action 通常是異步的,那么如何知道 action 什么時候結束呢?更重要的是,我們如何才能組合多個 action,以處理更加復雜的異步流程?

首先,你需要明白 store.dispatch 可以處理被觸發的 action 的處理函數返回的 Promise,并且 store.dispatch 仍舊返回 Promise:

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

現在你可以:

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

在另外一個 action 中也可以:

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

最后,如果我們利用 async / await,我們可以如下組合 action:

// 假設 getData() 和 getOtherData() 返回的是 Promise

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

一個 store.dispatch 在不同模塊中可以觸發多個 action 函數。在這種情況下,只有當所有觸發函數完成后,返回的 Promise 才會執行。

5 Module

這個部分以后再說吧,想了解的可以去官網看看先。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容

  • vuex是一個狀態管理模式,通過用戶的actions觸發事件,然后通過mutations去更改數據(你也可以說狀態...
    Ming_Hu閱讀 2,032評論 3 3
  • 系列文章:Vue 2.0 升(cai)級(keng)之旅Vuex — The core of Vue applic...
    6ed7563919d4閱讀 4,562評論 2 58
  • 安裝 npm npm install vuex --save 在一個模塊化的打包系統中,您必須顯式地通過Vue.u...
    蕭玄辭閱讀 2,960評論 0 7
  • Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,并以相應...
    白水螺絲閱讀 4,678評論 7 61
  • Vuex是什么? Vuex 是一個專為 Vue.js應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件...
    蕭玄辭閱讀 3,130評論 0 6