Vuex聽說很難荡陷?

Vuex 是什么狸膏?

Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式沟饥。它采用集中式存儲管理應(yīng)用的所有組件狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。

什么鬼東西

看完這段專業(yè)的解釋贤旷,我反正是一臉懵逼广料,內(nèi)心毫無波瀾,甚至有點(diǎn)想吃醬肘子幼驶。感覺和沒說一樣嘛艾杏!keep going。

什么是"狀態(tài)管理模式"盅藻?

一個簡單Vue計數(shù)器應(yīng)用

new Vue({
  // state
  data () {
    return {
      count: 0
    }
  },
  // view
  template: `
    <div>{{ count }}</div>
  `,
  // actions
  methods: {
    increment () {
      this.count++
    }
  }
})

這個狀態(tài)自管理應(yīng)用包含以下幾個部分:

  • state购桑,驅(qū)動應(yīng)用的數(shù)據(jù)源;
  • view氏淑,以聲明方式將 state 映射到視圖勃蜘;
  • actions,響應(yīng)在 view 上的用戶輸入導(dǎo)致的狀態(tài)變化假残。

這是一種“單向數(shù)據(jù)流”的理念缭贡。
然后說明一下這種理念的缺點(diǎn),當(dāng)我們的應(yīng)用遇到多個組件共享狀態(tài)時辉懒,單向數(shù)據(jù)流的簡潔性很容易被破壞:

  • 多個視圖依賴于同一狀態(tài)阳惹。
  • 來自不同視圖的行為需要變更同一狀態(tài)。

這里說的是耗帕,vue 組件之間的傳值操作,組件不多的情況下袱贮,嵌套的父子組件(prop仿便,emit,on)和兄弟組件(global event bus)還容易操作一些攒巍。但是如果項(xiàng)目龐大嗽仪,組件結(jié)構(gòu)復(fù)雜,組件間的數(shù)據(jù)傳遞會變得很困難柒莉,后期代碼不易維護(hù)闻坚。


若有所思

看到這里,貌似明白了些這鬼東西是要解決什么問題了兢孝。好的窿凤,然后我們繼續(xù)往“坑”里走。

最簡單的store

每一個 Vuex 應(yīng)用的核心就是 store(倉庫)跨蟹■ㄊ猓“store”基本上就是一個容器,它包含著你的應(yīng)用中大部分的狀態(tài) (state)窗轩。Vuex 和單純的全局對象有以下兩點(diǎn)不同:

  • Vuex 的狀態(tài)存儲是響應(yīng)式的夯秃。當(dāng) Vue 組件從 store 中讀取狀態(tài)的時候,若 store 中的狀態(tài)發(fā)生變化,那么相應(yīng)的組件也會相應(yīng)地得到高效更新仓洼。
  • 你不能直接改變 store 中的狀態(tài)介陶。改變 store 中的狀態(tài)的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態(tài)的變化色建,從而讓我們能夠?qū)崿F(xiàn)一些工具幫助我們更好地了解我們的應(yīng)用哺呜。
// 如果在模塊化構(gòu)建系統(tǒng)中,請確保在開頭調(diào)用了 Vue.use(Vuex)

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

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

store.commit('increment'); // 觸發(fā)修改變量的方法
console.log(store.state.count); // -> 1

WTF?這是啥漂羊,這不就是“全局變量”嘛驾锰!多個組件可能用到的數(shù)據(jù),存儲到 store(倉庫)里走越,但是這些數(shù)據(jù)是響應(yīng)式的椭豫,所以對這些數(shù)據(jù)進(jìn)行操作時,要遵守相應(yīng)的規(guī)則旨指。例如上面的例子赏酥。對 count 進(jìn)行修改,要通過 commit 來觸發(fā) mutation 里的方法對數(shù)據(jù)進(jìn)行操作谆构。

原來如此裸扶!

好了,喝口我的枸杞大補(bǔ)茶壓壓驚 [ 微笑 ]搬素。從這里再往坑里走呵晨,才是 Vuex 的一些核心概念,他們是:

  • State
  • Getter
  • Mutation
  • Action
  • Module

讓我們開始吧熬尺。

State

Vuex 使用單一狀態(tài)樹摸屠,用一個對象就包含了全部的應(yīng)用層級狀態(tài)。至此它便作為一個“唯一數(shù)據(jù)源 (SSOT)”而存在粱哼。這也意味著季二,每個應(yīng)用將僅僅包含一個 store 實(shí)例。單一狀態(tài)樹讓我們能夠直接地定位任一特定的狀態(tài)片段揭措,在調(diào)試的過程中也能輕易地取得整個當(dāng)前應(yīng)用狀態(tài)的快照胯舷。

這段話說的是一個 Vuex 只能包含一個store(存儲數(shù)據(jù)的庫),這樣你在訪問數(shù)據(jù)的時候好找些绊含。
store 的其中一個配置項(xiàng)就是 state 需纳。這個state吧,可以把他比作成vue實(shí)例中的data選項(xiàng)艺挪,就是把數(shù)據(jù)放在這里面不翩。

const state = {
  // 商品列表
  shopList:[{
    id: 1,
    name: '蘭博基尼',
    price: 10
  },{
    id: 2,
    name: '五菱宏光',
    price: 99999
  }],
  // 購物車列表
  addList: []
};

new Vuex.Store({
  state, // 這里是es6語法兵扬,相當(dāng)于  state: state
  ...
});

獲取商品列表 this.$store.state.shopList 就能看到了,或者使用輔助函數(shù)mapState口蝠,這樣雖然方便簡單器钟,建議新手先研究一下 es6 和方法原理,這里不做解釋妙蔗。

computed: mapState([
  // 映射 this.shopList為 store.state.shopList
  'shopList'
])

Vuex 文檔中有很多 es6 語法傲霸,如果有還不了解的同學(xué)們,可要抓緊上車嘍眉反。

拿下es6

Getter

我一度懷疑是不是文檔寫錯了昙啄,少加了一個s。(Getters)
這一項(xiàng)從字面意思上就可以看出寸五,他是用來取數(shù)據(jù)梳凛,得到數(shù)據(jù)的。有人會問梳杏,為什么不直接用this.$store.state.shopList這種方式直接拿呢韧拒。這種方式確實(shí)可以拿到,但是經(jīng)常會有一些情況是對拿到的數(shù)據(jù)做一些處理十性,例如格式化叛溢、過濾數(shù)據(jù)。劲适。楷掉。這時候就會用到 Getter 了。

const getters = {
    // 獲取id為2的商品
    shopid2(state){
        return state.shopList.find((el)=>{
            return el.id == 2;    
        });
    }
};

使用this.$store.getters.shopid2得到五菱宏光霞势,即可加入秋名山車神爭霸烹植,走上人生巔峰。
輔助函數(shù) mapGetters 方法:

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // 使用對象展開運(yùn)算符將 getter 混入 computed 對象中
    ...mapGetters([
      'shopid2'
      // ...
    ])
  }
}

Mutation

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

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

你需要以相應(yīng)的 type 調(diào)用 store.commit 方法:this.$store.commit('increment')

你想要動我的數(shù)據(jù)颂鸿,就得守我 Vuex 的規(guī)矩(順我者昌,逆我者亡)攒庵。
拿數(shù)據(jù)用 Getters 嘴纺,改數(shù)據(jù)用 mutation 。這個 mutation 有點(diǎn)像 vue 的生命周期鉤子函數(shù)浓冒,里面是一些方法栽渴,通過 this.$store.commit(typeName) 對應(yīng)的函數(shù)名稱,觸發(fā)對應(yīng)的函數(shù)稳懒。

提交載荷(Payload)

你可以向 store.commit 傳入額外的參數(shù)闲擦,即 mutation 的 載荷(payload):

// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
store.commit('increment', 10);


// 在大多數(shù)情況下,載荷應(yīng)該是一個對象,這樣可以包含多個字段并且記錄的 mutation 會更易讀
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
store.commit('increment', {
  amount: 10
})

輔助函數(shù) 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')`
    })
  }
}

在 Vuex 中墅冷,mutation 都是同步事務(wù)

store.commit('increment')
// 任何由 "increment" 導(dǎo)致的狀態(tài)變更都應(yīng)該在此刻完成纯路。

為了處理異步操作,讓我們繼續(xù)往下走 寞忿。

Action

Action 類似于 mutation驰唬,不同在于:

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

也就是說,Action 執(zhí)行的還是 Mutation 霹抛,只不過他可以異步執(zhí)行搓逾,差不多就是給 Mutation 外邊包了一層函數(shù)。


Action 異步操作 Mutation

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

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment'); // 在異步函數(shù)成功后調(diào)用 Mutation
    }, 1000)
  }
}

通過 this.$store.dispatch('incrementAsync')即可調(diào)用。
輔助函數(shù) mapActions 方法

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

Module

由于使用單一狀態(tài)樹裳食,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象矛市。當(dāng)應(yīng)用變得非常復(fù)雜時,store 對象就有可能變得相當(dāng)臃腫诲祸。
為了解決以上問題浊吏,Vuex 允許我們將 store 分割成模塊(module)。每個模塊擁有自己的 state救氯、mutation找田、action、getter着憨、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割:

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 的狀態(tài)
store.state.b // -> moduleB 的狀態(tài)

Module 的意思也不難理解墩衙,如果你有很多的狀態(tài),都寫在一起會比較亂甲抖,后邊也不好改漆改,你就可以給他們分個類跃赚,比如 moduleA 是我的購物車模塊的狀態(tài)管理返吻,moduleB 是我的商品信息模塊的狀態(tài)管理。
對于大型應(yīng)用风喇,我們會希望把 Vuex 相關(guān)代碼分割到模塊中柱衔。下面是項(xiàng)目結(jié)構(gòu)示例:

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API請求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我們組裝模塊并導(dǎo)出 store 的地方
    ├── actions.js        # 根級別的 action
    ├── mutations.js      # 根級別的 mutation
    └── modules
        ├── cart.js       # 購物車模塊
        └── products.js   # 產(chǎn)品模塊

什么時候用 Vuex樊破?

這個嘛愉棱,很簡單。你覺得你的項(xiàng)目里哲戚,各組件之間傳參什么的羽氮,相處的很和諧,那就無所謂用不用了惫恼。
當(dāng)你的組件兄弟們有傳參問題档押,有很多這個組件要用,那個組件要祈纯,其他好幾個組件也要令宿,要的你生不如死,你就可以考慮一下了腕窥。


我覺得可以考慮 Vuex

總結(jié)

最后粒没,我們再回顧一下 Vuex 都包含什么,是做什么用的簇爆。

store
俗稱倉庫癞松,倉庫里是你的模塊的數(shù)據(jù)、數(shù)據(jù)狀態(tài)入蛆、對數(shù)據(jù)做的操作响蓉,都在這個倉庫里。

state
倉庫里的數(shù)據(jù)都放到這個里面哨毁,很像 vue 的 data 枫甲。

getters
通常獲取可以用 this.$store.state.shopList 就可以拿到,但是如果你想對這項(xiàng)數(shù)據(jù)加工一下(格式化扼褪、過濾)想幻,就在 getters 里處理,然后通過 this.$store.getters.eventName 獲取话浇。

mutation
修改數(shù)據(jù)操作脏毯,你想對某項(xiàng)數(shù)據(jù)進(jìn)行修改,就得守規(guī)矩幔崖。不能用 this.$store.state.count=99食店,要把修改函數(shù)寫在 mutation 里面,用 this.commit('eventName') 觸發(fā)修改函數(shù)岖瑰。注意叛买,這里的操作必須是同步砂代。

action
異步觸發(fā) mutation 蹋订。

module
給多個狀態(tài)模塊分類。

文章若有錯誤之處刻伊,還請以官方文檔為準(zhǔn)露戒,也歡迎各位指出椒功,一起探討。

公眾號:前端很忙

做一個喜歡分享的前端開發(fā)者智什!

獲取更多干貨分享动漾,歡迎來搞!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荠锭,一起剝皮案震驚了整個濱河市旱眯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌证九,老刑警劉巖删豺,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異愧怜,居然都是意外死亡呀页,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門拥坛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蓬蝶,“玉大人,你說我怎么就攤上這事猜惋⊥璺眨” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵著摔,是天一觀的道長雪位。 經(jīng)常有香客問我,道長梨撞,這世上最難降的妖魔是什么雹洗? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮卧波,結(jié)果婚禮上时肿,老公的妹妹穿的比我還像新娘。我一直安慰自己港粱,他們只是感情好螃成,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著查坪,像睡著了一般寸宏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上偿曙,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天氮凝,我揣著相機(jī)與錄音,去河邊找鬼望忆。 笑死罩阵,一個胖子當(dāng)著我的面吹牛竿秆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播稿壁,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼幽钢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了傅是?” 一聲冷哼從身側(cè)響起匪燕,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎喧笔,沒想到半個月后谎懦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡溃斋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年界拦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梗劫。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡享甸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出梳侨,到底是詐尸還是另有隱情蛉威,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布走哺,位于F島的核電站蚯嫌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏丙躏。R本人自食惡果不足惜择示,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晒旅。 院中可真熱鬧栅盲,春花似錦、人聲如沸废恋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鱼鼓。三九已至拟烫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間迄本,已是汗流浹背硕淑。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人喜颁。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像曹阔,于是被迫代替她去往敵國和親半开。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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

  • 安裝 npm npm install vuex --save 在一個模塊化的打包系統(tǒng)中赃份,您必須顯式地通過Vue.u...
    蕭玄辭閱讀 2,926評論 0 7
  • Vuex是什么寂拆? Vuex 是一個專為 Vue.js應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲管理應(yīng)用的所有組件...
    蕭玄辭閱讀 3,106評論 0 6
  • Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式抓韩。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài)纠永,并以相應(yīng)...
    白水螺絲閱讀 4,652評論 7 61
  • vuex 場景重現(xiàn):一個用戶在注冊頁面注冊了手機(jī)號碼,跳轉(zhuǎn)到登錄頁面也想拿到這個手機(jī)號碼谒拴,你可以通過vue的組件化...
    sunny519111閱讀 8,008評論 4 111
  • 一在完成一個事項(xiàng)時最重要的是專注那件事其次才注重效率 記做事容易分心# 二理性請你上線情緒請你下線把每一件事情當(dāng)成...
    一只2b兔子閱讀 76評論 0 0