Vuex下Store的模組化拆分實踐

貓切發表於2016-12-02

前言

最近的專案用到了 vue.js + vuex + vue-router 全家桶,版本為 >2.0,在搞Store的時候發現,圈子裡大部分關於vuex的文章都是比較基礎的Demo搭建方式,很少有涉及到比較複雜的模組化拆分的Store實踐,而且事實上也有朋友在實踐中問到過這方面的內容,vuex自身提供了模組化的方式,因此在這裡總結一下我自己在專案裡的心得。

模組化拆分

vue.js的專案檔案結構在這裡就不說了,大家可以通過vue-cli初始化專案,腳手架會為你搭建一個start專案的最佳實踐。

預設你已經搭架好了一個專案,而且需要建立或者已經是一個複雜的Store,但是還沒有進行模組拆分,你可以嘗試對其進行模組拆分,當然在一開始你不必一定需要這麼做。

1. 安裝Vuex,建立檔案結構

在專案根目錄下安裝vuex:

npm install vuex -S

安裝完畢後,在專案的src資料夾下新建一個store資料夾,並且分別在其中新建modules,actions,mutations,getters,constants子資料夾和一個index.js檔案。

目錄結構如下:

└─ demo/
   ├── build/
   ├── config/
   ├── node_modules/
   ├── src/
   │   ├── assets/
   │   ├── components/
   │   ├── store/
   │   │   ├── actions/ 
   │   │   │   ├──aAction.js
   │   │   │   ├──bAction.js
   │   │   │   └──cAction.js
   │   │   ├── constants/
   │   │   │   └── types.js
   │   │   ├── getters/
   │   │   │   └── aGetter.js
   │   │   ├── modules/
   │   │   │   ├── aModules.js
   │   │   │   ├── bModules.js
   │   │   │   ├── cModules.js
   │   │   │   └── index.js
   │   │   ├── mutations/
   │   │   │   ├── aMutation.js
   │   │   │   ├── bMutation.js
   │   │   │   └── cMutation.js
   │   │   └── index.js
   │   ├── App.vue
   │   └── main.js
   ├── static/
   ├── utils/
   ├── test/
   └── index.html

好了,基本的檔案結構大概就是上面?這樣的。

2. 編寫模組A

在編寫模組之前,首先設定一些type類,例如:

types.js

module.exports = keyMirror({

    FETCH_LIST_REQUEST: null,
    FETCH_LIST_SUCCESS: null,
    FETCH_LISR_FAILURE: null
    
})

function keyMirror (obj) {
  if (obj instanceof Object) {
    var _obj = Object.assign({}, obj)
    var _keyArray = Object.keys(obj)
    _keyArray.forEach(key => _obj[key] = key)
    return _obj
  }
}

上面?自己實現keyMirror的方法,大家也可以使用下面這個包:
github.com/STRML/keyMirror

keyMirror的作用就是下面這個一個形式?,作用其實也不是很大:

Input: {key1: null, key2: null}

Output: {key1: key1, key2: key2}

actions/aAction.js

import { FETCH_LIST_REQUEST, FETCH_LIST_SUCCESS, FETCH_LISR_FAILURE } from  `../constants/types`
import { toQueryString } from `../../utils`
import axios from `axios`

export const fetchListAction = {
    fetchList ({ commit, state }, param) {
        commit(FETCH_LIST_REQUEST)
        axios.get(`http://youdomain.com/list`)
          .then(function (response) {
            commit(FETCH_LIST_SUCCESS, {
                data: response.data
            })  
            console.log(response);
          })
          .catch(function (error) {
            commit(FETCH_LIST_FAILURE, {
                error: error
            })
            console.log(error);
          });
    }
}

getters/aGetter.js

export const = fetchListGetter = {
    hotList (state) {
        return state.list.data.slice(0, 10)
    }
}

mutations/aMutation.js

import { FETCH_LIST_REQUEST, FETCH_LIST_SUCCESS, FETCH_LISR_FAILURE } from  `../constants/types`

export const fetchListMutation = {
    [FETCH_LIST_REQUEST] (state) {
        state.isFetching = true
    },
    [FETCH_LIST_SUCCESS] (state, action) {
        state.isFetching = false
        state.data = action.data
        state.lastUpdated = (new Date()).getTime()
    },
    [FETCH_LIST_FAILURE] (state, action) {
        state.isFetching = false
        state.error = action.error
    }
}

modules/aModule.js

import { fetchListAction } from `../actions/aAction`
import { fetchListGetter } from `../getters/aGetter`
import { fetchListMutation } from `../mutations/aMutation`

export const list = {
    state: {
        isFetching: false,
        data: []
    }
    actions: fetchListAction,
    getters: fetchListGetter,
    mutations: fetchListMutation
}

modules/index.js

import { list } from `./aModule`

module.exports = {
    list: list
}

3. 掛載store

index.js

import Vue from `vue`
import Vuex from `vuex`
import createLogger from `vuex/dist/logger`
import { list } from `./modules`

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    list: list
    
  },
  plugins: [createLogger()],
  strict: process.env.NODE_ENV !== `production`
})

if (module.hot) {
  module.hot.accept([`./mutations`], () => {
    const newMutations = require(`./mutations`).default

    store.hotUpdate({
      mutations: newMutations
    })
  })
}

export default store

4. store注入vue例項

main.js

  ····
import store from `./store`

  ···· 


var vue = new Vue({
  store,
 
  ···· 

})

vue.$mount(`#app`)

5. 在Component中使用

Vuex 提供了元件中使用的mapState,mapAction,mapGetter方法,因此可以很方便的呼叫。

Example.vue

<template>

 ·········

</template>
<script>
import { mapState, mapActions, mapGetters } from `vuex`
module.exports = {
    ·······
    methods: {
        ...mapActions([
            `fetchList`
        ])
    },
    computed: {
        ...mapState{
            list: state => state.list
        },
        ...mapGetters{[
            `hotList`
        ]}
    }
}
</script>
<style>
    ·······
</style>

複用模組

模組化拆分之後可以實現較為複雜的資料流,特別地,如果對action和mutation稍加改造,就可以複用模組:
比如我們在Example.vue中發起Action:

Example.vue

<template>

 ·········

</template>
<script>
import { mapState, mapActions, mapGetters } from `vuex`
module.exports = {
    ·······
    mounted () {
        this.fetchList({
            request: `week`
        })
    },
    methods: {
        ...mapActions([
            `fetchList`
        ])
    },
    computed: {
        ...mapState{
            list: state => state.list
        },
        ...mapGetters{[
            `hotList`
        ]}
    }
}
</script>
<style>
    ·······
</style>

在上面的例子中,我們在元件掛載完成之後發起了一個fetchList的action,並新增了一個名為request的引數,這裡給一個week值,也可以給按照業務需要給monthyear之類的值,接下來對aAction.js做一些修改。

actions/aAction.js

import { FETCH_LIST_REQUEST, FETCH_LIST_SUCCESS, FETCH_LISR_FAILURE } from  `../constants/types`
import { toQueryString } from `../../utils`
import axios from `axios`

export const fetchListAction = {
    fetchList ({ commit, state }, param) {
        commit(FETCH_LIST_REQUEST, {
            request: param[`request`]
        })
        axios.get(`http://youdomain.com/${param[`request`]}list`)
          .then(function (response) {
            commit(FETCH_LIST_SUCCESS, {
                request: param[`request`]
                data: response.data
            })  
            console.log(response);
          })
          .catch(function (error) {
            commit(FETCH_LIST_FAILURE, {
                request: param[`request`]
                error: error
            })
            console.log(error);
          });
    }
}

請求成功之後,在 commit()中加入了一個request的引數,這樣Mutation就可以從裡面獲取相應的引數,最後對aMutation做一些修改。

mutations/aMutation.js

import { FETCH_LIST_REQUEST, FETCH_LIST_SUCCESS, FETCH_LISR_FAILURE } from  `../constants/types`

export const fetchListMutation = {
    [FETCH_LIST_REQUEST] (state, action) {
        state[action.request].isFetching = true
    },
    [FETCH_LIST_SUCCESS] (state, action) {
        state[action.request].isFetching = false
        state[action.request].data = action.data
        state[action.request].lastUpdated = (new Date()).getTime()
    },
    [FETCH_LIST_FAILURE] (state, action) {
        state[action.request].isFetching = false
        state[action.request].error = action.error
    }
}

state加入了[action.request],以區分不同的介面資料。

完成以上修改後,只需要在元件呼叫相應的action時加入不同的引數,就可以呼叫相同型別但資料不同的介面。

總結

以上是我在Vuex實踐中總結的一些東西,分享給大家,如果有不合理或者錯誤❌的地方,也希望各位老司機不吝賜教??,有機會多交流。

微訊號:pasturn
Github:https://github.com/pasturn

相關文章