手撕Vuex-實現actions方法

BNTang發表於2023-11-01

經過上一篇章介紹,完成了實現 mutations 的功能,那麼接下來本篇將會實現 actions 的功能。

本篇我先介紹一下 actions 的作用,然後再介紹一下實現的思路,最後再實現程式碼。

actions 的作用是用來非同步修改共享資料的,怎麼非同步修改,這個時候我們回到 Vue 的官方 Vuex 文件中,有如下這麼一個圖:

從圖中可以看出,我們在元件中呼叫 dispatch 方法,然後 dispatch 方法會呼叫 actions,然後 actions 中的方法可以透過 commit 會呼叫 mutations 中的方法,最後 mutations 中的方法會修改 state 中的資料,這就是 actions 的作用。

這裡我們先來回顧一下怎麼使用 actions,再來實現一遍即可。

  1. 將官方的 Vuex 註釋放開
  2. 在 store 中定義 age
  3. 在 mutations 中定義 changeAge 方法
  4. 在 actions 中定義 asyncAddAge 方法 (頁面透過 dispatch 呼叫 actions 中的方法, actions 中的方法透過 commit 呼叫 mutations 中的方法)

如上是我本次實現的思路,接下來我們來實現程式碼。

我這裡直接貼出程式碼, 程式碼中有詳細的註釋, 程式碼如下:

1. 略過

2. 定義 age
state: {
    age: 0
}

3. 在 mutations 中定義 changeAge 方法
/**
 * 透過dispatch呼叫
 * @param state 倉庫的state
 * @param payload 載荷
 */
addAge(state, payload) {
    state.age += payload;
}

4. 在 actions 中定義 asyncAddAge 方法
/**
 * 透過dispatch呼叫
 * @param commit 提交
 * @param payload 載荷
 */
asyncAddAge({commit}, payload) {
    // 模擬非同步操作
    setTimeout(() => {
        // 透過commit呼叫mutations中的方法
        commit('addAge', payload);
    }, 3000);
}
  1. 在元件中透過 dispatch 呼叫 actions 中的方法(HelloWorld 元件)
// 顯示資料
<p>{{ this.$store.state.age }}</p>

// 呼叫actions中的方法
<button @click="myFn">我是按鈕</button>
myFn() {
  this.$store.dispatch('asyncAddAge', 10);
},

npm run serve 啟動專案,點選按鈕,3 秒後 age 的值加 10,說明 actions 的功能實現了。效果如下圖:

到此為止,回顧完成了之後,我們就可以開始實現 actions 的功能了。

actions 的實現思路和 mutations 的實現思路是一樣的,首先將官方的 Vuex 註釋掉,匯入我們自己的 Nuex:

import Vuex from './Nuex'
// import Vuex from 'vuex'

回到我們的 Nuex 檔案中,和之前一樣先將 actions 儲存到 Store 上,我這裡單獨弄了一個 initActions 方法,程式碼如下:

// 將傳遞進來的 actions 放到 Store 上
this.initActions(options);

initActions 方法的實現如下:

initActions(options) {
    // 1.拿到傳遞進來的actions
    let actions = options.actions || {};
    // 2.在Store上新增一個actions的屬性
    this.actions = {};
    // 3.將傳遞進來的actions中的方法新增到當前Store的actions上
    for (let key in actions) {
        this.actions[key] = (payload) => {
            // 4.將actions中的方法執行, 並且將當前Store例項傳遞過去
            actions[key](this, payload);
        }
    }
}

這裡和 mutations 的實現思路是一樣的,只是將 mutations 換成了 actions,然後將傳遞進來的 actions 中的方法新增到當前 Store 的 actions 上,最後將 actions 中的方法執行,並且將當前 Store 例項傳遞過去。

測試一下看看有沒有新增到 Store 上,執行專案,測試結果如下:

可以看到 actions 已經新增到 Store 上了,那麼在頁面上是透過 dispatch 呼叫 actions 中的方法,所以我們需要在 Store 上新增 dispatch 方法,程式碼如下:

dispatch = (type, payload) => {
    this.actions[type](payload);
}

這裡和 mutations 的實現思路是一樣的,只是將 commit 換成了 actions,然後將傳遞進來的 actions 中的方法執行,並且將當前 Store 例項傳遞過去。

執行專案,測試結果如下:

Uncaught TypeError: Cannot read properties of undefined (reading 'mutations')

這裡報錯了,其實這個問題我已經知道錯在哪裡了,我先帶著大家看一下這個呼叫流程,然後再解決這個問題。

  1. 在元件中呼叫 dispatch 方法,我傳遞是的 'asyncAddAge', 10

  1. 在 dispatch 方法中,我拿到了傳遞進來的 type,也就是 'asyncAddAge', payload 也就是 10, 然後呼叫了 actions 中的方法

  1. 在 actions 中的方法中,我拿到了傳遞進來的 payload,也就是 10,在 asyncAddAge 方法中,我呼叫了 commit 方法,也就是呼叫了 mutations 中的方法

  1. 在 commit 方法中,我拿到了傳遞進來的 type,也就是 'addAge', payload 也就是 10, 然後呼叫了 mutations 中的方法

報錯的位置在這裡,因為我在 commit 方法中,拿到了傳遞進來的 type,也就是 'addAge', 程式碼繼續往下執行,執行到 this.mutations[type](payload); this 是 undefined,所以報錯了。

正是因為在 actions 中的方法中,我呼叫了 commit 方法,也就是呼叫了 mutations 中的方法,在呼叫時沒有告訴 commit 方法,this 是誰,所以才會報錯。

那麼怎麼解決這個問題呢?其實很簡單,只需要將之前的 commit 方法改為箭頭函式即可,因為改為了箭頭函式,this 就是當前 Store 例項了(改為了箭頭函式當前在哪裡定義的那麼 this 就是誰),程式碼如下:

commit = (type, payload) => {
    this.mutations[type](payload);
}

執行專案,測試結果如下:

到此為止,actions 的功能就實現了,接下來我們來回顧一下實現的思路。

  1. 將傳遞進來的 actions 放到 Store 上
  2. 在 Store 上新增 dispatch 方法
  3. 在 dispatch 方法中,呼叫 actions 中的方法
  4. 在 actions 中的方法中,呼叫 commit 方法
  5. 在 commit 方法中,呼叫 mutations 中的方法(這裡就走我們上一篇章的流程了)

相關文章