vuex 閒置狀態重置方案

huangsw發表於2018-01-03

前言

大型單頁應用(後面都是指spa),我們往往會通過使用狀態管理器 vuex 去解決元件間狀態共享與狀態傳遞等問題。這種應用少則幾十個單頁,多則上百個單頁。隨著路由的頻繁切換,每個路由對應的 vuex 中的狀態將越來越多。為了做到頁面的極致優化,我們需要將那些閒置的狀態重置,以減小佔用的記憶體空間。

什麼狀態可以重置

vuex 強調採用集中式儲存管理應用的所有元件的狀態,但是我們真把所有的狀態都放到 store 中去處理,你會發現開發起來非常痛苦。這裡如果想很好的把控哪些資料需要放到 store 中去管理,首先要理解 vuex 是用來解決什麼問題的。vuex 官網指出是為了解決多個元件共享狀態的,那麼我們就可以把多個元件的共享狀態放到 store 中去管理,這裡的多元件共享對於單頁應用很多情況是跨路由的元件。如果 store只儲存多元件共享的狀態,那麼我們就沒必要去清理 vuex 中的狀態了,因為這些狀態隨時會被用到。

而隨著業務場景越來越複雜,很多與後臺互動的邏輯也都放到了元件中,這樣程式碼就變得很凌亂,vuex 也沒有被充分利用。這時我們可以把與後臺 api 互動的邏輯放到 vuex 中的action 去處理,後臺返回的狀態自然也就放到了 store 管理。這樣處理後,元件就只負責對資料進行渲染,邏輯非常清晰。而此時,元件對應的 store 中的狀態隨著路由的切換將會越來越多,而這些狀態就需要我們手動的去清理了。

很多方案都有取捨,如果將與後臺 api 互動的資料放到元件中,就沒必要去清理了,但是程式碼邏輯將變得比較亂。另外諸如 vuex 的外掛 vue-devtools 將無法監控到每次請求資料的變化...

什麼時候去重置狀態

我們想要的效果是在路由切換的時候,把上一個路由對應的 vuex 中的狀態重置掉,但是路由和vuex 並沒有一一對應的關係,如果要做到這種效果,那麼我們需要維護一個路由與vuex 模組的對應關係,這樣會很繁瑣。不如當路由改變時去重置 vuex 中的所有狀態。

vuex 中閒置狀態如何清理

下面將結合我的github例項去說明,這個例項建立了一個單頁應用,我們通過切換路由的時候將閒置的狀態清除。

改造路由對應元件的 module 狀態

例項中採用拆分 store 為多個 module 的方式,將路由對應的元件狀態放到對應的 module 中,多元件共享的狀態放到頂級的 store 中管理。大致如下:

// store/index.js
import page1 from "./modules/page1.js";
import page2 from "./modules/page2.js";
import page3 from "./modules/page3.js";
import page4 from "./modules/page4.js";
import page5 from "./modules/page5.js";

export default new Vuex.Store({
    state,
    getters,
    actions,
    mutations,
    modules: { // 每個路由對應的 module
        page1,
        page2,
        page3,
        page4,
        page5
    },
    plugins: __DEV__ ? [createLogger()] : [],
    strict: __DEV__ ? true : false
});
複製程式碼

路由 page1 對應的 module 的 state 形如:

// store/modules/page1.js
const state = {
     // 列表資料
     page1Data: [],
     // 標題資料
     page1Title: ''
}
複製程式碼

這些資料是通過呼叫後端 api 返回並複製的資料,如果我們在路由改變的時候重置這些資料,那麼需要將初始化資料提取出來,並且暴露一個需要重置的標識方法 initState(),代表路由改變的時候需要重置,當然這個方法名稱是個約定,你也可以定義為其他名稱。改造後為:

// store/modules/page1.js

// 放置你要重置的資料
const initState = {

    page1Data: [],
}

// state
const state = {
    // 引數解構
    ...initState,

    // 路由改變不想重置的資料
    page1Title: '',

    initState(){

        return initState
    }
}
複製程式碼

全域性 module 配置

定義全域性 mutation 事件型別

// store/types.js
export const RESET_STATES = 'resetStates'
複製程式碼

定義全域性 mutation

// store/mutation.js

import * as types from './types'

// 檢測所有的 state 並把 `initState()` 中的屬性重置
function resetState(state, moduleState) {

    const mState = state[moduleState];

    if (mState.initState && typeof mState.initState === 'function') {

        const initState = mState.initState();

        for (const key in initState) {

            mState[key] = initState[key];
        }
    }

}

export default {

    [types.RESET_STATES](state, payload) {

        for (const moduleState in state) {

            resetState(state, moduleState);
        }
    },

}
複製程式碼

定義全域性 action

// store/action.js
import * as types from './types'

export default {
    // rest state action
    resetStates:function (context, payLoad) {

        context.commit(types.RESET_STATES, payLoad);
    }
}
複製程式碼

路由切換觸發重置方法

至此一切準備就緒,只需要在路由改變時觸發重置的方法即可,在入口 vue 檔案中處理

// components/app.vue
<script>
    import {mapState, mapActions} from "vuex"
    export default{

        methods: {

            ...mapActions({
                resetStates: "resetStates"
            })
        },

        watch: {

            $route(to, from) {
                // 路由改變發起重置
                this.resetStates();
            }
        }
    }
</script>
複製程式碼

如果你的 chrome 瀏覽器安裝了 vuejs-devtools 在路由切換的時候就能夠很清晰的看到上一個路由資料的的重置過程。

總結

例項點這裡。我們這裡的 vuex 狀態重置,是每次路由切換遍歷所有的 store 中的狀態,並把initState() 中的屬性重置,如果能做到把當前的路由對應的 state 重置就更好了,但是路由和 store 中的 module 並沒有關聯關係。這裡只是提供一種重置 vuex 狀態的一種方案,如果有更好方案還請各位看官留言。如有不妥的地方也歡迎拍磚留言。

--完--

相關文章