Vuex4学习笔记
一、Vuex的狀態管理
二、Vuex的安裝
我們這里使用的是vuex4.x,安裝的時候需要添加 next 指定版本;
npm install vuex@next三、創建Store
每一個Vuex應用的核心就是store(倉庫):
- store本質上是一個容器,它包含著你的應用中大部分的狀態(state);
Vuex和單純的全局對象有什么區別呢?
第一:Vuex的狀態存儲是響應式的
- 當Vue組件從store中讀取狀態的時候,若store中的狀態發生變化,那么相應的組件也會被更新;
第二:你不能直接改變store中的狀態 - 改變store中的狀態的唯一途徑就顯示提交 (commit) mutation;
- 這樣使得我們可以方便的跟蹤每一個狀態的變化,從而讓我們能夠通過一些工具幫助我們更好的管理應用的狀態;
使用步驟:
-
創建Store對象;
-
在app中通過插件安裝;
計數器案例:
四、組件中使用store
在組件中使用store,我們按照如下的方式:
- 在模板中使用;
- 在options api中使用,比如computed;
- 在setup中使用;
五、組件獲取狀態
在前面我們已經學習過如何在組件中獲取狀態了。
當然,如果覺得那種方式有點繁瑣(表達式過長),我們可以使用計算屬性:
但是,如果我們有很多個狀態都需要獲取話,可以使用mapState的輔助函數:
- mapState的方式一:對象類型;
- mapState的方式二:數組類型;
- 也可以使用展開運算符和來原有的computed混合在一起;
六、在setup中使用mapState
在setup中如果我們單個獲取裝是非常簡單的:
-
通過useStore拿到store后去獲取某個狀態即可;
-
但是如果我們需要使用 mapState 的功能呢?
默認情況下,Vuex并沒有提供非常方便的使用mapState的方式,這里我們進行了一個函數的封裝:
useState.js:
七、getters的基本使用
某些屬性我們可能需要經過變化后來使用,這個時候可以使用getters:
getters可以接收第二個參數:
getters的返回函數
getters中的函數本身,可以返回一個函數,那么在使用的地方相當于可以調用這個函數:
mapGetters的輔助函數
這里我們也可以使用mapGetters的輔助函數。
在setup中使用:
封裝useGetters.js:
將前面封裝的useState和useGetters進行再次封裝:
八、Mutation基本使用
更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation:
Mutation攜帶數據
很多時候我們在提交mutation的時候,會攜帶一些數據,這個時候我們可以使用參數:
Mutation常量類型
定義常量:mutation-types.js
定義mutation:
提交mutation:
mapMutations輔助函數
我們也可以借助于輔助函數,幫助我們快速映射到對應的方法中:
在setup中使用也是一樣的:
mutation重要原則
一條重要的原則就是要記住 mutation 必須是同步函數
- 這是因為devtool工具會記錄mutation的日記;
- 每一條mutation被記錄,devtools都需要捕捉到前一狀態和后一狀態的快照;
- 但是在mutation中執行異步操作,就無法追蹤到數據的變化;
- 所以Vuex的重要原則中要求 mutation必須是同步函數;
九、actions的基本使用
Action類似于mutation,不同在于:
- Action提交的是mutation,而不是直接變更狀態;
- Action可以包含任意異步操作;
這里有一個非常重要的參數context:
-
context是一個和store實例均有相同方法和屬性的context對象;
-
所以我們可以從其中獲取到commit方法來提交一個mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters;
-
但是為什么它不是store對象呢?這個等到講Modules時再具體來說
actions的輔助函數
action也有對應的輔助函數:
- 對象類型的寫法;
- 數組類型的寫法;
actions的異步操作
Action 通常是異步的,那么如何知道 action 什么時候結束呢?
- 我們可以通過讓action返回Promise,在Promise的then中來處理完成后的操作;
十、module的基本使用
什么是Module?
- 由于使用單一狀態樹,應用的所有狀態會集中到一個比較大的對象,當應用變得非常復雜時,store 對象就有可能變得相當臃腫;
- 為了解決以上問題,Vuex 允許我們將 store 分割成模塊(module);
- 每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊;
module的命名空間
默認情況下,模塊內部的action和mutation仍然是注冊在全局的命名空間中的:
- 這樣使得多個模塊能夠對同一個 action 或 mutation 作出響應;
- Getter 同樣也默認注冊在全局命名空間;
未使用命名空間時:
如果我們希望模塊具有更高的封裝度和復用性,可以添加 namespaced: true 的方式使其成為帶命名空間的模塊:
- 當模塊被注冊后,它的所有 getter、action 及 mutation 都會自動根據模塊注冊的路徑調整命名;
加了命名空間后:
module的局部狀態
對于模塊內部的 mutation 和 getter,接收的第一個參數是模塊的局部狀態對象:
module修改或派發根組件
如果我們希望在home的action中修改root中的state,那么有如下的方式:
module的輔助函數
如果輔助函數有三種使用方法:
-
方式一:通過完整的模塊空間名稱來查找;
-
方式二:第一個參數傳入模塊空間名稱,后面寫上要使用的屬性;
-
方式三:通過 createNamespacedHelpers 生成一個模塊的輔助函數;
useMapper.js:
import {computed} from "vue"; import {useStore} from "vuex";export function useMapper(mapper, mapFn) {const store = useStore()const storeStateFns = mapFn(mapper) // {name: function, age: function}const storeState = {} // {name: ref, age: ref}Object.keys(storeStateFns).forEach((fnKey) => {const fn = storeStateFns[fnKey].bind({$store: store})storeState[fnKey] = computed(fn)})console.log(storeStateFns)console.log(storeState)return storeState }useState.js:
import {createNamespacedHelpers, mapState} from "vuex"; import {useMapper} from "./useMapper";export function useState(moduleName, mapper) {let mapperFn = mapStateif (typeof moduleName === 'string' && moduleName.length > 0) {mapperFn = createNamespacedHelpers(moduleName).mapState} else{mapperFn = moduleName}return useMapper(mapper, mapperFn) }useGetters.js:
import {createNamespacedHelpers, mapGetters} from "vuex"; import {useMapper} from "./useMapper";export function useGetters(moduleName, mapper) {let mapperFn = mapGettersif (typeof moduleName === 'string' && moduleName.length > 0) {// 處理這種情況:useGetters('home', ['increment'])mapperFn = createNamespacedHelpers(moduleName).mapGetters} else {mapperFn = moduleName // 處理這種情況:useGetters(['increment'])}return useMapper(mapper, mapperFn) }十一、nexttick
官方解釋:將回調推遲到下一個 DOM 更新周期之后執行。在更改了一些數據以等待 DOM 更新后立即使用它。
比如我們有下面的需求:
- 點擊一個按鈕,我們會修改在h2中顯示的message;
- message被修改后,獲取h2的高度;
實現上面的案例我們有三種方式:
-
方式一:在點擊按鈕后立即獲取到h2的高度(錯誤的做法)
-
方式二:在updated生命周期函數中獲取h2的高度(但是頁面其他數據更新,也會執行該操作)
-
方式三:使用nexttick函數;
nexttick是如何做到的呢?
總結
- 上一篇: 父组件访问子组件中的数据(父子组件通信案
- 下一篇: Vue3 Composition API