前言
最近的项目用到了 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
·········
复用模块
模块化拆分之后可以实现较为复杂的数据流,特别地,如果对action和mutation稍加改造,就可以复用模块:
比如我们在Example.vue中发起Action:
Example.vue
·········
在上面的例子中,我们在组件挂载完成之后发起了一个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