前言
最近的專案用到了 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
值,也可以給按照業務需要給month
、year
之類的值,接下來對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