1. Vuex是做什麼的?
- 官方解釋: Vuex 是一個專為Vue.js 應用程式開發的 狀態管理模式
- 狀態管理到底是什麼?
- 狀態管理模式、集中式儲存管理
- 簡單的將其看成把需要多個元件共享的變數全部儲存在一個物件裡面
- 然後, 將這個物件放在頂層的Vue例項中, 讓其他元件可以使用
- 那麼,多個元件是不是就可以共享這個物件中的所有變數屬性了呢?
1.1 管理什麼狀態?
- 有什麼狀態是我們需要在多個元件間共享的呢?
- 如果做過大型開發, 一定遇到過多個狀態, 在多個介面間的共享問題
- 比如使用者的登入狀態、使用者名稱稱、頭像、地理位置資訊等
- 比如商品的收藏、購物車中的物品等等
- 這些狀態資訊,我們都可以放在統一的地方, 對它進行儲存和管理, 並且它們還是響應式的
1.2 單頁面的狀態管理
我們知道, 要在單個元件中進行狀態管理是一件非常簡單的事情
- 什麼意思呢? 我們來看下面的圖片
這圖片中的三種東西, 怎麼理解呢?
- State: 不用多說, 就是我們的狀態, 姑且可以當做就是data 中的屬性
- View: 檢視層, 可以針對State的變化, 顯示不同的資訊
- Actions: 這裡的Actions 主要是使用者的各種操作: 點選,輸入等等, 會導致狀態的改變
1.3 單頁面狀態管理的實現
- 在這個案例中, 我們有沒有狀態需要管理呢? , 沒錯,就是個數counter
- counter 需要某種方式被記錄下來, 也就是我們的 State
- counter 目前的值需要被顯示在介面中, 也就是我們的View部分
- 介面發生某些操作時 (我們這裡是使用者的點選, 也可以是使用者的input ), 需要去更新狀態, 也就是我們的 Actions
- 這就是一個基本的單頁面狀態管理
1.4 多頁面狀態管理
Vue 已經幫我們做好了單個介面的狀態管理, 但是如果是多個介面呢?
- 多個檢視都依賴同一個狀態 (一個狀態改了, 多個介面需要進行更新)
- 不同介面的 Actions 都想修改同一個狀態 (Home,vue需要修改, Profile.vue 也需要修改這個狀態)
也就是說對於某些狀態 (狀態1\狀態2\狀態3) 來說只屬於我們某一個檢視, 但是也有一些狀態( 狀態a\狀態b\狀態c ) 屬於多個檢視共同想要維護的
- 狀態1, 狀態2, 狀態3 你放在自己的房間中 ,你自己管理自己用, 沒問題
- 但是狀態a 狀態b 狀態c 我們希望交給一個大管家來統一幫助我們管理
- Vuex 就是為我們提供這個大管家的工具
全域性單例模式 (大管家)
- 我們現在要做的就是將共享的狀態抽取出來, 交給我們的大管家, 統一進行管理
- 之後, 你們每個檢視, 按照我 規定好的 規定, 進行訪問和修改等操作
- 這就是 Vuex 背後的基本思想
1.5 Vuex 狀態管理圖例
1.6 簡單的案例
首先, 我們需要在某個地方存放我們的Vuex 程式碼:
- 這裡, 我們先建立一個資料夾 store , 並且在其中建立一個 index.js檔案
- 在index.js檔案中寫入如下程式碼
其次, 我們讓所有的Vue元件都可以使用這個
store
物件來到
main.js
檔案, 匯入store
物件, 並且放在new Vue
中這樣, 在其他的Vue元件中, 我們就可以通過
$store
的方式, 獲取到這個store
物件了[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-ifhPbJqh-1630130671735)(img/image-20210609114408524.png)]
使用Vuex 的 content
1.7 使用步驟
- 提取出一個公共的 store物件, 用於儲存在多個元件中共享的狀態
- 將 store 物件放置在 new Vue 物件中, 這樣可以保證在所有的元件中都可以使用到
- 在其他元件中使用 store 物件中儲存的狀態即可
- 通過
this.$store.state.屬性
的方式來訪問狀態 - 通過
this.$store.commit( 'mutation中方法' )
來修改狀態
- 通過
- 注意事項:
- 我們通過提交
mutation
的方式, 而非直接改變store.state.count
- 這是因為Vuex可以更明確的追蹤狀態的變化, 所以不要直接改變
store.state.count
的值
- 我們通過提交
2. Vuex 核心概念
- Vuex有幾個比較核心的概念:
- State
- Getters
- Mutation
- Action
- Module
2.1 State 單一狀態樹
- Vuex提出使用單一狀態樹, 什麼是單一狀態樹呢?
- 英文名稱是 Single Source of Truth, 也可以翻譯成單一資料來源
- 但是, 它是什麼呢? 我們來看一個生活中的例子
- OK, 我用一個生活中的例子做一個簡單的類比
- 我們知道, 在國內我們有很多的資訊需要被記錄, 比如上學時的個人檔案, 工作後的社保記錄, 公積金記錄,結婚後的婚姻資訊,以及其他相關的戶口, 醫療, 文憑, 房產記錄等等
- 這些資訊被分散在很多地方進行管理, 有一天你需要辦某個業務時, (比如入戶某個城市), 你會發現你需要到各個對應的工作地點去列印, 蓋章各種資料資訊, 最後到一個地方提交證明你的資訊五五
- 這種儲存資訊的方案, 不僅僅低效, 而且不方便管理, 以及日後的維護也是一個龐大的工程(需要大量的各個部門的人力來維護)
- 這個和我們在應用開發中比較類似:
- 如果你的狀態資訊是儲存到多個Store物件中的, 那麼之後的管理和維護等等都會變得特別困難
- 所以Vuex 也使用了單一狀態樹來管理應用層級的全部狀態
- 單一狀態樹能夠讓我們最直接的方式找到某個狀態的片段, 而且在之後的維護和除錯過程中, 也可以非常方便的管理和維護
2.2 Getter 基本使用
有時候,我們需要從store中獲取一些state變異後的狀態,比如下面的Store中:
// 獲取 state 平方後的值
const store = new Vuex.Store({
state: {
content: 1000
}
}
我們可以在 Store 中定義 getters
const store = new Vuex.Store({
state: {
content: 1000
},
getters: {
powerContent(state) {
return state.content * state.content;
}
}
})在元件中呼叫
<template>
<div id="app">
<h2>-----------app元件 getters 相關資訊-------------</h2>
<p>content的平方{{ $store.getters.powerContent }}</p>
</div>
</template> <script>
import HelloVuex from "./components/HelloVuex"; export default {
name: 'App',
components: {
HelloVuex
}
}
</script> <style> </style>
2.2.1 Getters 作為引數和傳遞引數
如果我們已經有了一個獲取所有年齡大於20歲學生列表的 getter, 那麼程式碼可以這樣來寫
getters預設是不能傳遞引數的, 如果希望傳遞引數, 那麼只能讓getter 本身返回另一個函式
比如上面的案例中, 我們希望根據 age 獲取使用者資訊
2.3 Mutation 狀態更新
Vuex 的store 狀態的更新唯一方式: 提交Mutation
Mutation主要包括兩部分:
- 字串的 事件型別(type)
- 一個回撥函式(handler) ,該回調函式的第一個引數就是 state
mutation 的定義方式:
通過mutation更新狀態
2.3.1 Mutation傳遞引數
在通過mutation 更新資料的時候, 有可能我們希望攜帶一些額外的引數
- 引數被稱為是 mutation 的載荷(Payload)
Mutation中的程式碼:
元件中的程式碼:
但是如果引數不是一個呢?
- 比如我們有很多引數需要傳遞
- 這個時候, 我們通常會以物件的形式傳遞, 也就是payload是一個物件
- 這個時候可以再從物件中取出相關的資訊
methods: {
addCount(count){
this.$store.commit({
type:'increaseCount',
count
})
}
}
mutations: {
increaseCount(state, payload) {
console.log(payload);
state.content += payload.count;
}
}
2.3.2 Mutation 響應規則
Vuex 的store 中的state是響應式的, 當state中的資料發生改變時, Vue元件會自動更新
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-bzlo1b3S-1630130671741)(img/屬性被新增到響應式系統中.jpg)]
這就要求我們必須遵守一些Vuex對應的規則:
- 提前在store中初始化好所需的屬性
- 當給 state中的物件新增新屬性時, 使用下面的方式:
- 方式一: 使用
Vue.set(obj,'newProp',123);
- 方式二: 用新物件給舊物件重新賦值
- 方式一: 使用
- 當要刪除 state中的屬性時, 使用 Vue.delete
Vue.delete(state,'key')
2.3.3 Mutation 常量型別 - 概念
- 我們來考慮下面的問題:
- 在mutation中, 我們定義了很多事件型別(也就是其中的方法名稱)
- 當我們的專案增大時, Vuex管理的狀態越來越多, 需要更新狀態的情況越來越多, 那麼意味著Mutation 中的方法越來越多
- 方法過多, 使用者需要花費大量的精力去記住這些方法, 甚至是多個檔案間來回切換, 檢視方法名稱, 甚至如果不是複製的時候, 可能還會出現寫錯的情況
- 如何避免上述的問題呢?
- 在各種 Flux 視線中, 一種很常見的方案就是使用常量 替代 Mutation 事件的型別
- 我們可以將這些常量放在一個單獨的檔案中, 方便管理以及讓整個app所有的事件型別一目瞭然
- 具體怎麼做呢?
- 我們可以建立一個檔案: mutation-types.js , 並且在其中定義我們的常量
- 定義常量時, 我們可以使用ES2015中的風格, 使用一個常量來作為函式的名稱
2.3.4 Mutation 常量型別 - 程式碼
2.3.5 Mutation 同步函式
通常情況下, Vuex 要求我們 Mutation中的方法必須是同步方法
- 主要的原因是當我們使用devtools除錯工具時, devtools 可以幫助我們捕捉 mutation的快照
- 但是如果是非同步操作, 那麼devtools將不能很好的追蹤這個操作什麼時候會被完成
比如我們之前的程式碼, 當執行更新時, devtools 中會有如下資訊
但是, 如果Vuex中的程式碼使用了非同步函式
你會發現state中的info資料一直沒有被改變, 因為它無法追蹤到
2.4 Action 的基本定義
我們強調, 不要在Mutation中進行非同步操作
- 但是某些情況, 我們確實希望在Vuex 中進行一些非同步操作, 比如網路請求, 必然是非同步的, 這個時候怎麼處理呢?
- Action 類似於Mutation, 但是是用來代替Mutation進行非同步操作的
Action的基本使用程式碼如下:
context 是什麼?
- context 是和store 物件具有相同方法和屬性的物件
- 也就是說, 我們可以通過context 去進行commit 相關的操作, 也可以獲取 context, state等
- 但是注意, 這裡它們並不是同一個物件, 為什麼呢? 我們後面學習Modules的時候,再具體說
這樣的程式碼是否多此一舉呢?
- 我們定義了actions, 然後又在actions中去進行commit, 這不是脫褲放屁嗎?
- 事實上並不是這樣, 如果在Vuex 中有非同步操作, 那麼我們可以在actions 中完成了
2.4.1 Action 的分發
在Vue元件中, 如果我們呼叫action中的方法, 那麼就需要使用dispatch
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳
同樣的, 也是支援傳遞payload
2.4.2 Action 返回的 Promise
前面我們學習ES6語法的時候說過, Promise經常用於非同步操作
- 在Action中, 我們可以將非同步操作放在一個Promise中, 並且在成功或者失敗後, 呼叫對應的resolve或reject
ok , 我們來看下面的程式碼:
2.5 認識Modules
Modules 是模組的意思, 為什麼在Vuex 中我們要使用模組呢?
- Vue使用單一狀態樹, 那麼也意味著很多狀態都會交給Vuex來管理
- 當應用變得非常複雜時, store物件就有可能變的相當臃腫
- 為了解決這個問題, Vuex 允許我們將store分割成模組(Module), 而每個模組擁有自己的 state、mutations、actions、getters等
我們按照什麼樣的方式來組織模組呢?
2.5.1 Module 區域性狀態
- 上面的程式碼中, 我們已經有了整體的組織結構, 下面我們來看看具體的區域性模組中的程式碼如何書寫
- 我們在moduleA中新增state、mutations、getters
- mutation 和 getters 接收的第一個引數是區域性狀態物件
- 注意:
- 雖然, 我們的 doubleCount 和 increment 都是定義在物件內部的
- 但是在呼叫的時候, 依然是通過
this.$store
來呼叫的
專案結構
- 當我們的Vuex 幫助我們管理過多的內容時, 好的專案結構可以讓我們的程式碼更加清晰
2.5.1 Module 區域性狀態
上面的程式碼中, 我們已經有了整體的組織結構, 下面我們來看看具體的區域性模組中的程式碼如何書寫
- 我們在moduleA中新增state、mutations、getters
- mutation 和 getters 接收的第一個引數是區域性狀態物件
注意:
- 雖然, 我們的 doubleCount 和 increment 都是定義在物件內部的
- 但是在呼叫的時候, 依然是通過
this.$store
來呼叫的
專案結構
當我們的Vuex 幫助我們管理過多的內容時, 好的專案結構可以讓我們的程式碼更加清晰