
Vuex 簡(jiǎn)介
Vuex 是Vue.js 應(yīng)用程序的狀態(tài)管理模式 + 庫(kù)。它充當(dāng)應(yīng)用程序中所有組件的集中存儲(chǔ),其規(guī)則確保狀態(tài)只能以可預(yù)測(cè)的方式改變。它還與 Vue 的官方devtools 擴(kuò)展集成?(打開新窗口)提供高級(jí)功能,例如零配置時(shí)間旅行調(diào)試和狀態(tài)快照導(dǎo)出/導(dǎo)入。
vuex原理
讓我們從一個(gè)簡(jiǎn)單的 Vue 計(jì)數(shù)器應(yīng)用程序開始:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
它是一個(gè)獨(dú)立的應(yīng)用程序,包含以下部分:
- 的狀態(tài),真理,推動(dòng)我們的應(yīng)用程序的來(lái)源;
- 的視圖,所述的聲明性映射狀態(tài);
- 的動(dòng)作,可能的方式的狀態(tài)可以從反應(yīng)于用戶輸入改變視圖。
這是“單向數(shù)據(jù)流”概念的簡(jiǎn)單表示:

然而,當(dāng)我們有多個(gè)共享一個(gè)公共狀態(tài)的組件時(shí),這種簡(jiǎn)單性很快就會(huì)崩潰:
- 多個(gè)視圖可能依賴于同一個(gè)狀態(tài)。
- 來(lái)自不同視圖的操作可能需要改變相同的狀態(tài)。
對(duì)于問(wèn)題一,傳遞 props 對(duì)于深度嵌套的組件可能很乏味,而且對(duì)于兄弟組件根本不起作用。對(duì)于問(wèn)題二,我們經(jīng)常發(fā)現(xiàn)自己求助于解決方案,例如獲取直接的父/子實(shí)例引用或嘗試通過(guò)事件來(lái)改變和同步多個(gè)狀態(tài)副本。這兩種模式都很脆弱,很快就會(huì)導(dǎo)致無(wú)法維護(hù)的代碼。
那么為什么我們不從組件中提取共享狀態(tài),并在全局單例中管理它呢?這樣,我們的組件樹就變成了一個(gè)大“視圖”,任何組件都可以訪問(wèn)狀態(tài)或觸發(fā)動(dòng)作,無(wú)論它們?cè)跇渲械哪膫€(gè)位置!
通過(guò)定義和分離狀態(tài)管理中涉及的概念并強(qiáng)制執(zhí)行維護(hù)視圖和狀態(tài)之間獨(dú)立性的規(guī)則,我們?yōu)槲覀兊拇a提供了更多的結(jié)構(gòu)和可維護(hù)性。
這是 Vuex 背后的基本思想,受Flux啟發(fā)?(?與其他模式不同,Vuex 也是一個(gè)專門為 Vue.js 量身定制的庫(kù)實(shí)現(xiàn),以利用其粒度反應(yīng)系統(tǒng)進(jìn)行高效更新。
如果你想以交互方式學(xué)習(xí) Vuex,你可以在 Scrimba 上查看這個(gè)Vuex 課程?(打開新窗口),它為您提供了截屏視頻和代碼游樂(lè)場(chǎng)的組合,您可以隨時(shí)暫停和播放。

#我應(yīng)該什么時(shí)候使用它?
Vuex 幫助我們以更多的概念和樣板為代價(jià)來(lái)處理共享狀態(tài)管理。這是短期和長(zhǎng)期生產(chǎn)力之間的權(quán)衡。
如果您從未構(gòu)建過(guò)大型 SPA 并直接跳入 Vuex,那可能會(huì)感到冗長(zhǎng)和令人生畏。這是完全正常的 - 如果您的應(yīng)用程序很簡(jiǎn)單,那么您很可能沒有 Vuex 也可以。一個(gè)簡(jiǎn)單的存儲(chǔ)模式?(打開新窗口)可能就是你所需要的。但是,如果您正在構(gòu)建一個(gè)中大型 SPA,您可能會(huì)遇到一些情況,讓您考慮如何更好地處理 Vue 組件之外的狀態(tài),而 Vuex 將是您的下一步。Redux的作者Dan Abramov有一個(gè)很好的報(bào)價(jià):
Flux 庫(kù)就像眼鏡:你會(huì)知道什么時(shí)候需要它們。
Vuex 是 Vue.js 的官方狀態(tài)管理庫(kù)。它的工作是在應(yīng)用程序的組件之間共享數(shù)據(jù)。開箱即用的 Vue.js 中的組件可以使用
- props,將狀態(tài)從父組件傳遞給子組件
- events,從子組件更改父組件的狀態(tài),或使用根組件作為事件總線
有時(shí)事情會(huì)變得比這些簡(jiǎn)單選項(xiàng)所允許的更復(fù)雜。
在這種情況下,一個(gè)不錯(cuò)的選擇是將狀態(tài)集中在一個(gè)商店中。這就是 Vuex 所做的。
為什么要使用 Vuex
Vuex 不是您可以在 Vue 中使用的唯一狀態(tài)管理選項(xiàng)(您也可以使用Redux),但它的主要優(yōu)點(diǎn)是它是官方的,并且它與 Vue.js 的集成使它發(fā)光。
使用React,您將不得不選擇眾多可用庫(kù)中的一種,因?yàn)樵撋鷳B(tài)系統(tǒng)龐大且沒有實(shí)際標(biāo)準(zhǔn)。最近 Redux 是最受歡迎的選擇,MobX 在流行度方面緊隨其后。使用 Vue,我什至要說(shuō)除了 Vuex 之外,您不需要四處尋找任何東西,尤其是在開始時(shí)。
Vuex 借鑒了 React 生態(tài)系統(tǒng)的許多想法,因?yàn)檫@是 Redux 推廣的 Flux 模式。
如果您已經(jīng)了解 Flux 或 Redux,那么 Vuex 會(huì)非常熟悉。如果你不這樣做,沒問(wèn)題 - 我會(huì)從頭開始解釋每個(gè)概念。
Vue 應(yīng)用程序中的組件可以有自己的狀態(tài)。例如,輸入框會(huì)將輸入的數(shù)據(jù)存儲(chǔ)在本地。這非常好,即使使用 Vuex,組件也可以具有本地狀態(tài)。
你知道當(dāng)你開始做大量工作來(lái)傳遞一個(gè)狀態(tài)時(shí),你需要像 Vuex 這樣的東西。
在這種情況下,Vuex 為狀態(tài)提供了一個(gè)中央存儲(chǔ)庫(kù),您可以通過(guò)要求存儲(chǔ)來(lái)改變狀態(tài)。
每個(gè)依賴于特定狀態(tài)部分的組件都將使用 store 上的 getter 訪問(wèn)它,這確保它在事情發(fā)生變化時(shí)立即更新。
使用 Vuex 會(huì)給應(yīng)用程序帶來(lái)一些復(fù)雜性,因?yàn)樾枰阅撤N方式設(shè)置才能正常工作,但是如果這有助于解決無(wú)組織的 props 傳遞和事件系統(tǒng),如果過(guò)于復(fù)雜,可能會(huì)變成意大利面條式的混亂,那么它就是一個(gè)不錯(cuò)的選擇。
開始吧
在這個(gè)例子中,我從一個(gè)Vue CLI應(yīng)用程序開始。Vuex 也可以通過(guò)直接將其加載到腳本標(biāo)簽中來(lái)使用,但由于 Vuex 更適合更大的應(yīng)用程序,因此您更有可能將它用于更結(jié)構(gòu)化的應(yīng)用程序,例如您可以使用 Vue 快速啟動(dòng)的應(yīng)用程序命令行界面。
我使用的示例將放在 CodeSandbox,這是一個(gè)很棒的服務(wù),它有一個(gè) Vue CLI 示例,可以在https://codesandbox.io/s/vue 上使用。我建議用它來(lái)玩。

到達(dá)那里后,單擊“添加依賴項(xiàng)”按鈕,輸入“vuex”并單擊它。
現(xiàn)在 Vuex 將列在項(xiàng)目依賴項(xiàng)中。
要在本地安裝 Vuex,您可以運(yùn)行npm install vuex或yarn add vuex在項(xiàng)目文件夾中。
創(chuàng)建 Vuex 商店
現(xiàn)在,我們準(zhǔn)備創(chuàng)建Vuex商店。
這個(gè)文件可以放在任何地方。一般建議把它放在src/store/store.js文件中,所以我們會(huì)這樣做。
在這個(gè)文件中,我們初始化 Vuex 并告訴 Vue 使用它:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({})

我們導(dǎo)出一個(gè) Vuex 存儲(chǔ)對(duì)象,我們使用Vuex.Store()API創(chuàng)建該對(duì)象。
商店的用例
現(xiàn)在我們有了一個(gè)框架,讓我們?yōu)?Vuex 的一個(gè)好的用例提出一個(gè)想法,這樣我就可以介紹它的概念。
例如,我有 2 個(gè)同級(jí)組件,一個(gè)帶有輸入字段,另一個(gè)打印輸入字段內(nèi)容。
當(dāng)輸入字段更改時(shí),我還想更改第二個(gè)組件中的內(nèi)容。非常簡(jiǎn)單,但這將為我們完成工作。
介紹我們需要的新組件
我刪除了 HelloWorld 組件并添加了一個(gè) Form 組件和一個(gè) Display 組件。
<template>
<div>
<label for="flavor">Favorite ice cream flavor?</label>
<input name="flavor">
</div>
</template><template>
<div>
<p>You chose ???</p>
</div>
</template>將這些組件添加到應(yīng)用程序
我們將它們添加到 App.vue 代碼而不是 HelloWorld 組件中:
<template>
<div id="app">
<Form/>
<Display/>
</div>
</template>
<script>
import Form from './components/Form'
import Display from './components/Display'
export default {
name: 'App',
components: {
Form,
Display
}
}
</script>將狀態(tài)添加到商店
有了這個(gè),我們回到 store.js 文件,我們向名為 的商店添加一個(gè)屬性state,它是一個(gè)包含該flavor屬性的對(duì)象。那最初是一個(gè)空字符串。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
flavor: ''
}
})
當(dāng)用戶輸入輸入字段時(shí),我們將更新它。
添加突變
除非使用突變,否則無(wú)法操縱狀態(tài)。我們?cè)O(shè)置了一個(gè)將在 Form 組件中使用的變更來(lái)通知商店?duì)顟B(tài)應(yīng)該改變。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
flavor: ''
},
mutations: {
change(state, flavor) {
state.flavor = flavor
}
}
})
添加一個(gè) getter 來(lái)引用狀態(tài)屬性
有了這個(gè)集合,我們需要添加一種查看狀態(tài)的方法。我們使用getter 來(lái)做到這一點(diǎn)。我們?yōu)樵?code>flavor屬性設(shè)置了一個(gè) getter?:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
flavor: ''
},
mutations: {
change(state, flavor) {
state.flavor = flavor
}
},
getters: {
flavor: state => state.flavor
}
})
注意getters對(duì)象如何。flavor是這個(gè)對(duì)象的一個(gè)??屬性,它接受狀態(tài)作為參數(shù),并返回flavor狀態(tài)的屬性。
將Vuex商店添加到應(yīng)用程序
現(xiàn)在該商店已準(zhǔn)備就緒,可以使用了。我們回到我們的應(yīng)用程序代碼,在 main.js 文件中,我們需要導(dǎo)入狀態(tài)并使其在我們的 Vue 應(yīng)用程序中可用。
我們?cè)黾?/p>
import { store } from './store/store'
我們將它添加到 Vue 應(yīng)用程序中:
new Vue({
el: '#app',
store,
components: { App },
template: '<App/>'
})
一旦我們添加了它,因?yàn)檫@是主要的 Vue 組件,store每個(gè) Vue 組件內(nèi)的變量都將指向 Vuex 存儲(chǔ)。
使用提交更新用戶操作的狀態(tài)
讓我們?cè)谟脩糨斎雰?nèi)容時(shí)更新狀態(tài)。
我們通過(guò)使用store.commit()API來(lái)做到這一點(diǎn)。
但首先,讓我們創(chuàng)建一個(gè)在輸入內(nèi)容更改時(shí)調(diào)用的方法。我們使用@input而不是@change因?yàn)楹笳邇H在焦點(diǎn)移離輸入框時(shí)觸發(fā),而@input在每次按鍵時(shí)調(diào)用。
<template>
<div>
<label for="flavor">Favorite ice cream flavor?</label>
<input @input="changed" name="flavor">
</div>
</template>
<script>
export default {
methods: {
changed: function(event) {
alert(event.target.value)
}
}
}
</script>
現(xiàn)在我們有了風(fēng)味的價(jià)值,我們使用 Vuex API:
<script>
export default {
methods: {
changed: function(event) {
this.$store.commit('change', event.target.value)
}
}
}
</script>
看看我們?nèi)绾问褂?code>this.$store??這要?dú)w功于在主要Vue組件初始化中包含了存儲(chǔ)對(duì)象。
該commit()方法接受一個(gè)突變名稱(我們change在Vuex存儲(chǔ)中使用了)和一個(gè)有效負(fù)載,這些負(fù)載將作為回調(diào)函數(shù)的第二個(gè)參數(shù)傳遞給該突變。
使用 getter 打印狀態(tài)值
現(xiàn)在我們需要在 Display 模板中引用這個(gè)值的 getter,使用$store.getters.flavor.?this可以刪除,因?yàn)槲覀冊(cè)谀0逯校⑶?code>this是隱式的。
<template>
<div>
<p>You chose {{ $store.getters.flavor }}</p>
</div>
</template>包起來(lái)
Vuex 的介紹到此結(jié)束!












