最近的项目用到了 vue.js + vuex + vue-router 全家桶,版本为 >2.0,在搞Store的时候发现,圈子里大部分关于vuex的文章都是比较基础的Demo搭建方式,很少有涉及到比较复杂的模块化拆分的Store实践,而且事实上也有朋友在实践中问到过这方面的内容,vuex自身提供了模块化的方式,因此在这里总结一下我自己在项目里的心得。
vue.js的项目文件结构在这里就不说了,大家可以通过 vue-cli
初始化项目,脚手架会为你搭建一个start项目的最佳实践。
默认你已经搭架好了一个项目,而且需要建立或者已经是一个复杂的Store,但是还没有进行模块拆分,你可以尝试对其进行模块拆分,当然在一开始你不必一定需要这么做。
在项目根目录下安装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
好了,基本的文件结构大概就是上面:point_up_2:这样的。
在编写模块之前,首先设定一些type类,例如:
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 } }
上面:point_up_2:自己实现keyMirror的方法,大家也可以使用下面这个包:
github.com/STRML/keyMirrorkeyMirror的作用就是下面这个一个形式,作用其实也不是很大:
Input: {key1: null, key2: null}
Output: {key1: key1, key2: key2}
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); }); } }
export const = fetchListGetter = { hotList (state) { return state.fetchList.data.slice(0, 10) } }
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 } }
import { fetchListAction } from '../actions/aAction' import { fetchListGetter } from '../getter/aGetter' import { fetchListMutation } from '../mutation/aMutation' export const list = { state: { isFetching: false, data: [] } actions: fetchListAction, getters: fetchListGetter, mutations: fetchListMutation }
import { list } from './aModule' module.exports = { list: list }
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(['./modules'], () => { const newMutations = require('./modules').default store.hotUpdate({ mutations: newMutations }) }) } export default store
···· import store from './store' ···· var vue = new Vue({ store, ···· }) vue.$mount('#app')
Vue 提供了组件中使用的 mapState
, mapAction
, mapGetter
方法,因此可以很方便的调用。
<template> ········· </template> <script> import { mapState, mapActions, mapGetters } from 'vuex' module.exports = { ······· methods: { ...mapActions([ 'fetchList' ]) }, computed: { ...mapState{ list: state => state.list }, ...mapGetter{[ 'hotList' ]} } } </script> <style> ······· </style>
模块化拆分之后可以实现较为复杂的数据流,特别地,如果对action和mutation稍加改造,就可以复用模块:
比如我们在Example.vue中发起Action:
<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 }, ...mapGetter{[ 'hotList' ]} } } </script> <style> ······· </style>
在上面的例子中,我们在组件挂载完成之后发起了一个fetchList的action,并添加了一个名为 week
的参数,接下来对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做一些修改。
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实践中总结的一些东西,分享给大家,如果有不合理或者错误:x:的地方,也希望各位老司机不吝赐教:pray::pray:,有机会多交流。
微信号:pasturn
Github: https://github.com/pasturn