Vuex 簡介
Vuex 是Vue.js 應(yīng)用程序的狀態(tài)管理模式 + 庫。它充當(dāng)應(yīng)用程序中所有組件的集中存儲,其規(guī)則確保狀態(tài)只能以可預(yù)測的方式改變。它還與 Vue 的官方devtools 擴(kuò)展集成?(打開新窗口)提供高級功能,例如零配置時間旅行調(diào)試和狀態(tài)快照導(dǎo)出/導(dǎo)入。
vuex原理
讓我們從一個簡單的 Vue 計(jì)數(shù)器應(yīng)用程序開始:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
它是一個獨(dú)立的應(yīng)用程序,包含以下部分:
- 的狀態(tài),真理,推動我們的應(yīng)用程序的來源;
- 的視圖,所述的聲明性映射狀態(tài);
- 的動作,可能的方式的狀態(tài)可以從反應(yīng)于用戶輸入改變視圖。
這是“單向數(shù)據(jù)流”概念的簡單表示:
然而,當(dāng)我們有多個共享一個公共狀態(tài)的組件時,這種簡單性很快就會崩潰:
- 多個視圖可能依賴于同一個狀態(tài)。
- 來自不同視圖的操作可能需要改變相同的狀態(tài)。
對于問題一,傳遞 props 對于深度嵌套的組件可能很乏味,而且對于兄弟組件根本不起作用。對于問題二,我們經(jīng)常發(fā)現(xiàn)自己求助于解決方案,例如獲取直接的父/子實(shí)例引用或嘗試通過事件來改變和同步多個狀態(tài)副本。這兩種模式都很脆弱,很快就會導(dǎo)致無法維護(hù)的代碼。
那么為什么我們不從組件中提取共享狀態(tài),并在全局單例中管理它呢?這樣,我們的組件樹就變成了一個大“視圖”,任何組件都可以訪問狀態(tài)或觸發(fā)動作,無論它們在樹中的哪個位置!
通過定義和分離狀態(tài)管理中涉及的概念并強(qiáng)制執(zhí)行維護(hù)視圖和狀態(tài)之間獨(dú)立性的規(guī)則,我們?yōu)槲覀兊拇a提供了更多的結(jié)構(gòu)和可維護(hù)性。
這是 Vuex 背后的基本思想,受Flux啟發(fā)?(?與其他模式不同,Vuex 也是一個專門為 Vue.js 量身定制的庫實(shí)現(xiàn),以利用其粒度反應(yīng)系統(tǒng)進(jìn)行高效更新。
如果你想以交互方式學(xué)習(xí) Vuex,你可以在 Scrimba 上查看這個Vuex 課程?(打開新窗口),它為您提供了截屏視頻和代碼游樂場的組合,您可以隨時暫停和播放。
#我應(yīng)該什么時候使用它?
Vuex 幫助我們以更多的概念和樣板為代價來處理共享狀態(tài)管理。這是短期和長期生產(chǎn)力之間的權(quán)衡。
如果您從未構(gòu)建過大型 SPA 并直接跳入 Vuex,那可能會感到冗長和令人生畏。這是完全正常的 - 如果您的應(yīng)用程序很簡單,那么您很可能沒有 Vuex 也可以。一個簡單的存儲模式?(打開新窗口)可能就是你所需要的。但是,如果您正在構(gòu)建一個中大型 SPA,您可能會遇到一些情況,讓您考慮如何更好地處理 Vue 組件之外的狀態(tài),而 Vuex 將是您的下一步。Redux的作者Dan Abramov有一個很好的報價:
Flux 庫就像眼鏡:你會知道什么時候需要它們。
Vuex 是 Vue.js 的官方狀態(tài)管理庫。它的工作是在應(yīng)用程序的組件之間共享數(shù)據(jù)。開箱即用的 Vue.js 中的組件可以使用
- props,將狀態(tài)從父組件傳遞給子組件
- events,從子組件更改父組件的狀態(tài),或使用根組件作為事件總線
有時事情會變得比這些簡單選項(xiàng)所允許的更復(fù)雜。
在這種情況下,一個不錯的選擇是將狀態(tài)集中在一個商店中。這就是 Vuex 所做的。
為什么要使用 Vuex
Vuex 不是您可以在 Vue 中使用的唯一狀態(tài)管理選項(xiàng)(您也可以使用Redux),但它的主要優(yōu)點(diǎn)是它是官方的,并且它與 Vue.js 的集成使它發(fā)光。
使用React,您將不得不選擇眾多可用庫中的一種,因?yàn)樵撋鷳B(tài)系統(tǒng)龐大且沒有實(shí)際標(biāo)準(zhǔn)。最近 Redux 是最受歡迎的選擇,MobX 在流行度方面緊隨其后。使用 Vue,我什至要說除了 Vuex 之外,您不需要四處尋找任何東西,尤其是在開始時。
Vuex 借鑒了 React 生態(tài)系統(tǒng)的許多想法,因?yàn)檫@是 Redux 推廣的 Flux 模式。
如果您已經(jīng)了解 Flux 或 Redux,那么 Vuex 會非常熟悉。如果你不這樣做,沒問題 - 我會從頭開始解釋每個概念。
Vue 應(yīng)用程序中的組件可以有自己的狀態(tài)。例如,輸入框會將輸入的數(shù)據(jù)存儲在本地。這非常好,即使使用 Vuex,組件也可以具有本地狀態(tài)。
你知道當(dāng)你開始做大量工作來傳遞一個狀態(tài)時,你需要像 Vuex 這樣的東西。
在這種情況下,Vuex 為狀態(tài)提供了一個中央存儲庫,您可以通過要求存儲來改變狀態(tài)。
每個依賴于特定狀態(tài)部分的組件都將使用 store 上的 getter 訪問它,這確保它在事情發(fā)生變化時立即更新。
使用 Vuex 會給應(yīng)用程序帶來一些復(fù)雜性,因?yàn)樾枰阅撤N方式設(shè)置才能正常工作,但是如果這有助于解決無組織的 props 傳遞和事件系統(tǒng),如果過于復(fù)雜,可能會變成意大利面條式的混亂,那么它就是一個不錯的選擇。
開始吧
在這個例子中,我從一個Vue CLI應(yīng)用程序開始。Vuex 也可以通過直接將其加載到腳本標(biāo)簽中來使用,但由于 Vuex 更適合更大的應(yīng)用程序,因此您更有可能將它用于更結(jié)構(gòu)化的應(yīng)用程序,例如您可以使用 Vue 快速啟動的應(yīng)用程序命令行界面。
我使用的示例將放在 CodeSandbox,這是一個很棒的服務(wù),它有一個 Vue CLI 示例,可以在https://codesandbox.io/s/vue 上使用。我建議用它來玩。
到達(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商店。
這個文件可以放在任何地方。一般建議把它放在src/store/store.js
文件中,所以我們會這樣做。
在這個文件中,我們初始化 Vuex 并告訴 Vue 使用它:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({})
我們導(dǎo)出一個 Vuex 存儲對象,我們使用Vuex.Store()
API創(chuàng)建該對象。
商店的用例
現(xiàn)在我們有了一個框架,讓我們?yōu)?Vuex 的一個好的用例提出一個想法,這樣我就可以介紹它的概念。
例如,我有 2 個同級組件,一個帶有輸入字段,另一個打印輸入字段內(nèi)容。
當(dāng)輸入字段更改時,我還想更改第二個組件中的內(nèi)容。非常簡單,但這將為我們完成工作。
介紹我們需要的新組件
我刪除了 HelloWorld 組件并添加了一個 Form 組件和一個 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)添加到商店
有了這個,我們回到 store.js 文件,我們向名為 的商店添加一個屬性state
,它是一個包含該flavor
屬性的對象。那最初是一個空字符串。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
flavor: ''
}
})
當(dāng)用戶輸入輸入字段時,我們將更新它。
添加突變
除非使用突變,否則無法操縱狀態(tài)。我們設(shè)置了一個將在 Form 組件中使用的變更來通知商店?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
}
}
})
添加一個 getter 來引用狀態(tài)屬性
有了這個集合,我們需要添加一種查看狀態(tài)的方法。我們使用getter 來做到這一點(diǎn)。我們?yōu)樵?code>flavor屬性設(shè)置了一個 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
對象如何。flavor
是這個對象的一個??屬性,它接受狀態(tài)作為參數(shù),并返回flavor
狀態(tài)的屬性。
將Vuex商店添加到應(yīng)用程序
現(xiàn)在該商店已準(zhǔn)備就緒,可以使用了。我們回到我們的應(yīng)用程序代碼,在 main.js 文件中,我們需要導(dǎo)入狀態(tài)并使其在我們的 Vue 應(yīng)用程序中可用。
我們增加
import { store } from './store/store'
我們將它添加到 Vue 應(yīng)用程序中:
new Vue({
el: '#app',
store,
components: { App },
template: '<App/>'
})
一旦我們添加了它,因?yàn)檫@是主要的 Vue 組件,store
每個 Vue 組件內(nèi)的變量都將指向 Vuex 存儲。
使用提交更新用戶操作的狀態(tài)
讓我們在用戶輸入內(nèi)容時更新狀態(tài)。
我們通過使用store.commit()
API來做到這一點(diǎn)。
但首先,讓我們創(chuàng)建一個在輸入內(nèi)容更改時調(diào)用的方法。我們使用@input
而不是@change
因?yàn)楹笳邇H在焦點(diǎn)移離輸入框時觸發(fā),而@input
在每次按鍵時調(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)味的價值,我們使用 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組件初始化中包含了存儲對象。
該commit()
方法接受一個突變名稱(我們change
在Vuex存儲中使用了)和一個有效負(fù)載,這些負(fù)載將作為回調(diào)函數(shù)的第二個參數(shù)傳遞給該突變。
使用 getter 打印狀態(tài)值
現(xiàn)在我們需要在 Display 模板中引用這個值的 getter,使用$store.getters.flavor
.?this
可以刪除,因?yàn)槲覀冊谀0逯?,并?code>this是隱式的。
<template>
<div>
<p>You chose {{ $store.getters.flavor }}</p>
</div>
</template>
包起來
Vuex 的介紹到此結(jié)束!