Vue学习笔记(5)(Vuex)
Vue2.x學習筆記。原視頻教程:最全最新Vue、Vuejs教程,從入門到精通_嗶哩嗶哩 (゜-゜)つロ 干杯~-bilibili
Vuex
認識Vuex
官方解釋:Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。
-
它采用 集中式存儲管理 應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化。
-
Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能。
狀態管理到底是什么?
-
狀態管理模式、集中式存儲管理這些名詞聽起來就非常高大上,讓人捉摸不透。
-
其實,你可以簡單的將其看成把需要多個組件共享的變量全部存儲在一個對象里面。
-
然后,將這個對象放在頂層的Vue實例中,讓其他組件可以使用。
-
那么,多個組件是不是就可以共享這個對象中的所有變量屬性了呢?
等等,如果是這樣的話,為什么官方還要專門出一個插件Vuex呢?難道我們不能自己封裝一個對象來管理嗎?
-
當然可以,只是我們要先想想VueJS帶給我們最大的便利是什么呢?沒錯,就是響應式。
-
如果你自己封裝實現一個對象能不能保證它里面所有的屬性做到響應式呢?當然也可以,只是自己封裝可能稍微麻煩一些。
-
不用懷疑,Vuex就是為了提供這樣一個在多個組件間共享狀態的插件,用它就可以了。
但是,有什么狀態時需要我們在多個組件間共享的呢?
-
如果你做過大型開放,你一定遇到過多個狀態,在多個界面間的共享問題。
-
比如用戶的登錄狀態、用戶名稱、頭像、地理位置信息等等。
-
比如商品的收藏、購物車中的物品等等。
-
這些狀態信息,我們都可以放在統一的地方,對它進行保存和管理,而且它們還是響應式的。
單界面的狀態管理
我們知道,要在單個組件中進行狀態管理是一件非常簡單的事情
什么意思呢?我們來看下面的圖片。
這圖片中的三種東西,怎么理解呢?
-
State:不用多說,就是我們的狀態。(你姑且可以當做就是data中的屬性)
-
View:視圖層,可以針對State的變化,顯示不同的信息。(這個好理解吧?)
-
Actions:這里的Actions主要是用戶的各種操作:點擊、輸入等等,會導致狀態的改變。
寫點代碼,加深理解:
-
在這個案例中,我們有木有狀態需要管理呢?沒錯,就是個數counter。
-
counter需要某種方式被記錄下來,也就是我們的State。
-
counter目前的值需要被顯示在界面中,也就是我們的View部分。
-
界面發生某些操作時(我們這里是用戶的點擊,也可以是用戶的input),需要去更新狀態,也就是我們的Actions
多界面狀態管理
Vue已經幫我們做好了單個界面的狀態管理,但是如果是多個界面呢?
-
多個試圖都依賴同一個狀態(一個狀態改了,多個界面需要進行更新)
-
不同界面的Actions都想修改同一個狀態(Home.vue需要修改,Profile.vue也需要修改這個狀態)
也就是說對于某些狀態(狀態1/狀態2/狀態3)來說只屬于我們某一個試圖,但是也有一些狀態(狀態a/狀態b/狀態c)屬于多個試圖共同想要維護的
-
狀態1/狀態2/狀態3你放在自己的房間中,你自己管理自己用,沒問題。
-
但是狀態a/狀態b/狀態c我們希望交給一個大管家來統一幫助我們管理!!!
-
沒錯,Vuex就是為我們提供這個大管家的工具。
全局單例模式(大管家)
-
我們現在要做的就是將共享的狀態抽取出來,交給我們的大管家,統一進行管理。
-
之后,你們每個試圖,按照我規定好的規定,進行訪問和修改等操作。
-
這就是Vuex背后的基本思想。
Vuex基本使用
0. 安裝:
npm install vuex --save1. 提取公共store對象,保存共享狀態
首先,我們需要在某個地方存放我們的Vuex代碼:
這里,我們先創建一個文件夾store,并且在其中創建一個index.js文件
在index.js文件中寫入如下代碼:
import Vuex from 'vuex' import Vue from 'vue'Vue.use(Vuex)const store = new Vuex.store({state: {},mutations: {} //需要共享的狀態 })export default store2. 掛載到Vue實例中
其次,我們讓所有的Vue組件都可以使用這個store對象
-
來到main.js文件,導入store對象,并且放在new Vue中
-
這樣,在其他Vue組件中,我們就可以通過this.$store的方式,獲取到這個store對象了
3. 使用store對象
在其他組件中使用store對象中保存的狀態即可
-
通過this.$store.state.屬性的方式來訪問狀態
-
通過this.$store.commit('mutation中方法')來修改狀態
Vuex核心概念
state
Vuex提出使用單一狀態樹, 什么是單一狀態樹呢?
- 英文名稱是Single Source of Truth,也可以翻譯成單一數據源。
但是,它是什么呢?我們用一個生活中的例子做一個簡單的類比:
-
我們知道,在國內我們有很多的信息需要被記錄,比如上學時的個人檔案,工作后的社保記錄,公積金記錄,結婚后的婚姻信息,以及其他相關的戶口、醫療、文憑、房產記錄等等(還有很多信息)。
-
這些信息被分散在很多地方進行管理,有一天你需要辦某個業務時(比如入戶某個城市),你會發現你需要到各個對應的工作地點去打印、蓋章各種資料信息,最后到一個地方提交證明你的信息無誤。
-
這種保存信息的方案,不僅僅低效,而且不方便管理,以及日后的維護也是一個龐大的工作(需要大量的各個部門的人力來維護,當然國家目前已經在完善我們的這個系統了)。
這個和我們在應用開發中比較類似:
-
如果你的狀態信息是保存到多個Store對象中的,那么之后的管理和維護等等都會變得特別困難。
-
所以Vuex也使用了單一狀態樹來管理應用層級的全部狀態。
-
單一狀態樹能夠讓我們最直接的方式找到某個狀態的片段,而且在之后的維護和調試過程中,也可以非常方便的管理和維護。
Getters
基本使用
有時候,我們需要從store中獲取一些state變異后的狀態,比如下面的Store中:
-
獲取學生年齡大于20的個數:
-
我們可以在Store中定義getters
- 組件中使用computed:
- store中使用getters:
作為參數和傳遞參數
如果我們已經有了一個獲取所有年齡大于20歲學生列表的getters, 那么代碼可以這樣來寫:
getters默認是不能傳遞參數的, 如果希望傳遞參數, 那么只能讓getters本身返回另一個函數。
-
比如上面的案例中,我們希望根據ID獲取用戶的信息
Mutations
Vuex的store狀態的更新唯一方式:提交Mutation
Mutation狀態更新
Mutation主要包括兩部分:
-
字符串的事件類型(type)
-
一個回調函數(handler),該回調函數的第一個參數就是state。
mutation的定義方式:
//store.vueconst store = new Vuex.store({state: {count:100},//定義方法mutations: {increment(state) {state.count++}} })通過mutation更新:
//App.vue//<button @click="addition">+</button>methods: {addition() {//提交mutation方法this.$store.commit('increment')} }Mutation傳遞參數
在通過mutation更新數據的時候, 有可能我們希望攜帶一些額外的參數。參數被稱為是mutation的載荷(Payload)。
一個參數:
//mutation中定義 decrement(state, n) {state.count -= n } //methods中提交 subtraction() {this.$store.commit('decrement', 2) }多個參數:
這個時候, 我們通常會以對象的形式傳遞, 也就是payload是一個對象。這個時候可以再從對象中取出相關的信息。
changeCount(state, payload) {state.count = payload.count } changeCount() {this.$store.commit('changeCount', {count: 0}) }Mutation提交風格
上面的通過commit進行提交是一種普通的方式。Vue還提供了另外一種風格, 它是一個包含type屬性的對象:
this.$store.commit({type: 'changeCount',count: 100 })Mutation中的處理方式是將整個commit的對象作為payload使用, 所以代碼沒有改變, 依然如下:
changeCount(state,payload) {state.count = payload.count }Mutation響應規則
Vuex的store中的state是響應式的, 當state中的數據發生改變時, Vue組件會自動更新。
state中的這些屬性都會被加入到響應式系統中,而響應式系統會監聽屬性的變化,當屬性發生變化時,會通知所有界面中應用到這些屬性的地方,讓界面發生刷新。
這就要求我們必須遵守一些Vuex對應的規則:
-
提前在store中初始化好所需的屬性。
-
當給state中的對象添加新屬性時, 使用下面的方式:
- 方式一: 使用Vue.set(obj, 'newProp', 123)
- 方式二: 用新對象給舊對象重新賦值
我們來看一個例子:
當我們點擊更新信息時, 界面并沒有發生對應改變。
如何才能讓它改變呢?
查看下面代碼的方式一和方式二,都可以讓state中的屬性是響應式的:
mutation: {updateInfo(state,payload) {//state.info['height'] = payload.height//方式一:Vue.set()Vue.set(state.info, 'height', payload.height)//方式二:給info賦值一個新對象state.info = {...state.info, 'height': payload.height}} }Mutation的類型常量
我們來考慮下面的問題:
-
在mutation中, 我們定義了很多事件類型(也就是其中的方法名稱)。
-
當我們的項目增大時, Vuex管理的狀態越來越多, 需要更新狀態的情況越來越多, 那么意味著Mutation中的方法越來越多。
-
方法過多, 使用者需要花費大量的經歷去記住這些方法, 甚至是多個文件間來回切換, 查看方法名稱, 甚至如果不是復制的時候, 可能還會出現寫錯的情況。
如何避免上述的問題呢?
-
在各種Flux實現中, 一種很常見的方案就是使用常量替代Mutation事件的類型。
-
我們可以將這些常量放在一個單獨的文件中, 方便管理以及讓整個app所有的事件類型一目了然。
具體怎么做呢?
-
我們可以創建一個文件: mutation-types.js, 并且在其中定義我們的常量:
-
定義常量時, 我們可以使用ES2015中的風格, 使用一個常量來作為函數的名稱:
Mutation同步函數
通常情況下, Vuex要求我們Mutation中的方法必須是同步方法。
主要的原因是當我們使用devtools時, 可以devtools可以幫助我們捕捉mutation的快照。但是如果是異步操作, 那么devtools將不能很好的追蹤這個操作什么時候會被完成。
比如:
你會發現state中的info數據一直沒有被改變, 因為他無法追蹤到。
So,通常情況下不要在mutation中進行異步的操作。
Actions
基本定義
我們強調, 不要在Mutation中進行異步操作。
但是某些情況, 我們確實希望在Vuex中進行一些異步操作, 比如網絡請求, 必然是異步的。這個時候怎么處理呢?
Action類似于Mutation, 但是是用來代替Mutation進行異步操作的。
Action的基本使用代碼如下:
const store = new Vuex.Store({state: {count: 0},mutations: {increment(state) {state.count++}},actions: {increment(context) {context.commit('increment')}} })context是什么?
-
context是和store對象具有相同方法和屬性的對象。
-
也就是說, 我們可以通過context去進行commit相關的操作, 也可以獲取context.state等。
-
但是注意, 這里它們并不是同一個對象, 為什么呢? 我們后面學習Modules的時候, 再具體說。
我們定義了actions,,然后又在actions中去進行commit,這樣的代碼是否多此一舉呢?事實上并不是這樣, 如果在Vuex中有異步操作, 那么我們就可以在actions中完成了。
Action的分發
在Vue組件中, 如果我們調用action中的方法, 那么就需要使用dispatch:
methods: {increment() {this.$store.dispatch('increment')} }同樣的, 也是支持傳遞payload:
methods: {increment() {this.$store.dispatch('increment', {cCount: 5})} } mutations: {increment(state,payload) {state.count += payload.cCount} }, actions: {increment(context, payload) {setTimeout(() => {context.commit('increment', payload)}, 5000)} }Action返回的Promise
在Action中, 我們可以將異步操作放在一個Promise中, 并且在成功或者失敗后, 調用對應的resolve或reject:
Module
Module是模塊的意思, 為什么在Vuex中我們要使用模塊呢?
Vue使用單一狀態樹,那么也意味著很多狀態都會交給Vuex來管理。
當應用變得非常復雜時,store對象就有可能變得相當臃腫。
為了解決這個問題,Vuex允許我們將store分割成模塊(Module), 而每個模塊擁有自己的state、mutation、action、getters等。
我們按照什么樣的方式來組織模塊呢? 我們來看下邊的代碼:
上面的代碼中, 我們已經有了整體的組織結構, 下面我們來看看具體的局部模塊中的代碼如何書寫。
-
我們在moduleA中添加state、mutations、getters
-
mutation和getters接收的第一個參數是局部狀態對象
注意:
雖然,我們的doubleCount和increment都是定義在對象內部的,但是在調用的時候, 依然是通過this.$store來直接調用的。
Actions的寫法
actions的寫法呢? 接收一個context參數對象,局部狀態通過 context.state 暴露出來,根節點狀態則為 context.rootState:
如果getters中也需要使用全局的狀態, 可以接受更多的參數:
項目結構
當我們的Vuex幫助我們管理過多的內容時, 好的項目結構可以讓我們的代碼更加清晰。
總結
以上是生活随笔為你收集整理的Vue学习笔记(5)(Vuex)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ECSHOP 模版文件里的编辑区域
- 下一篇: Nuxt --- 也来说说vue服务端渲