作为高级前端开发工程师,我将为你提供一个针对 Uniapp + Vue2 + Vuex 的 Store 二次封装方案,使团队成员能够更便捷地使用和管理状态。
封装目标
-
模块化管理状态
-
简化调用方式
-
提供类型提示(在 Vue2 中尽可能实现)
-
便于维护和查找
封装方案
1. 目录结构重构
首先,我们重构 store 的目录结构:
store/
├── index.js # 主入口文件
├── modules/ # 模块目录
│ ├── user.js # 用户模块
│ ├── cart.js # 购物车模块
│ └── ... # 其他模块
└── store-utils.js # 封装的工具方法
2. 主入口文件封装 (store/index.js)
javascript
import Vue from 'vue'
import Vuex from 'vuex'
import * as utils from './store-utils'
Vue.use(Vuex)
// 自动加载 modules 目录下的所有模块
const modules = utils.autoLoadModules()
const store = new Vuex.Store({
modules,
// 全局配置
strict: process.env.NODE_ENV !== 'production'
})
// 挂载工具方法到 Vue 原型
Vue.prototype.$storeUtils = utils
export default store
3. 工具方法封装 (store/store-utils.js)
javascript
/**
* 自动加载 modules 目录下的所有模块
*/
export function autoLoadModules() {
const modules = {}
const requireModule = require.context(
// 搜索目录
'./modules',
// 是否搜索子目录
false,
// 匹配文件的正则
/\.js$/
)
requireModule.keys().forEach(fileName => {
// 获取模块名(去掉文件扩展名)
const moduleName = fileName.replace(/(\.\/|\.js)/g, '')
modules[moduleName] = {
namespaced: true,
...requireModule(fileName).default
}
})
return modules
}
/**
* 简化 mapState 调用
* @param {string} moduleName 模块名
* @param {Array|Object} states 状态名数组或对象
*/
export function mapState(moduleName, states) {
if (!moduleName) return {}
if (Array.isArray(states)) {
const result = {}
states.forEach(key => {
result[key] = function() {
return this.$store.state[moduleName][key]
}
})
return result
}
// 对象形式
const result = {}
Object.keys(states).forEach(key => {
result[key] = function() {
return this.$store.state[moduleName][states[key]]
}
})
return result
}
/**
* 简化 mapGetters 调用
* @param {string} moduleName 模块名
* @param {Array|Object} getters getter名数组或对象
*/
export function mapGetters(moduleName, getters) {
if (!moduleName) return {}
const namespace = moduleName + '/'
if (Array.isArray(getters)) {
const result = {}
getters.forEach(getterName => {
result[getterName] = function() {
return this.$store.getters[namespace + getterName]
}
})
return result
}
// 对象形式
const result = {}
Object.keys(getters).forEach(key => {
result[key] = function() {
return this.$store.getters[namespace + getters[key]]
}
})
return result
}
/**
* 简化 mapMutations 调用
* @param {string} moduleName 模块名
* @param {Array|Object} mutations mutation名数组或对象
*/
export function mapMutations(moduleName, mutations) {
if (!moduleName) return {}
const namespace = moduleName + '/'
if (Array.isArray(mutations)) {
const result = {}
mutations.forEach(mutationName => {
result[mutationName] = function(payload) {
this.$store.commit(namespace + mutationName, payload)
}
})
return result
}
// 对象形式
const result = {}
Object.keys(mutations).forEach(key => {
result[key] = function(payload) {
this.$store.commit(namespace + mutations[key], payload)
}
})
return result
}
/**
* 简化 mapActions 调用
* @param {string} moduleName 模块名
* @param {Array|Object} actions action名数组或对象
*/
export function mapActions(moduleName, actions) {
if (!moduleName) return {}
const namespace = moduleName + '/'
if (Array.isArray(actions)) {
const result = {}
actions.forEach(actionName => {
result[actionName] = function(payload) {
return this.$store.dispatch(namespace + actionName, payload)
}
})
return result
}
// 对象形式
const result = {}
Object.keys(actions).forEach(key => {
result[key] = function(payload) {
return this.$store.dispatch(namespace + actions[key], payload)
}
})
return result
}
/**
* 创建模块的快捷访问方式
* @param {string} moduleName 模块名
*/
export function createModuleHelpers(moduleName) {
return {
state: (stateName) => {
return this.$store.state[moduleName][stateName]
},
getter: (getterName) => {
return this.$store.getters[`${moduleName}/${getterName}`]
},
commit: (mutationName, payload) => {
this.$store.commit(`${moduleName}/${mutationName}`, payload)
},
dispatch: (actionName, payload) => {
return this.$store.dispatch(`${moduleName}/${actionName}`, payload)
}
}
}
4. 模块示例 (store/modules/user.js)
javascript
const state = {
token: '',
userInfo: null,
isLogin: false
}
const getters = {
getUserName: state => {
return state.userInfo?.name || ''
},
getUserId: state => {
return state.userInfo?.id || ''
}
}
const mutations = {
SET_TOKEN(state, token) {
state.token = token
},
SET_USER_INFO(state, userInfo) {
state.userInfo = userInfo
state.isLogin = !!userInfo
},
CLEAR_USER(state) {
state.token = ''
state.userInfo = null
state.isLogin = false
}
}
const actions = {
login({ commit }, { username, password }) {
return new Promise((resolve, reject) => {
// 模拟登录请求
uni.request({
url: '/api/login',
method: 'POST',
data: { username, password },
success: (res) => {
commit('SET_TOKEN', res.data.token)
commit('SET_USER_INFO', res.data.userInfo)
resolve(res.data)
},
fail: (err) => {
reject(err)
}
})
})
},
logout({ commit }) {
commit('CLEAR_USER')
return Promise.resolve()
}
}
export default {
state,
getters,
mutations,
actions
}
使用方式
1. 组件中使用
javascript
import { mapState, mapGetters, mapMutations, mapActions } from '@/store/store-utils'
export default {
computed: {
// 传统方式
...mapState('user', ['token', 'userInfo', 'isLogin']),
...mapGetters('user', ['getUserName', 'getUserId']),
// 或者使用对象形式
...mapState('user', {
myToken: 'token',
myUserInfo: 'userInfo'
})
},
methods: {
...mapMutations('user', ['SET_USER_INFO']),
...mapActions('user', ['login', 'logout']),
// 示例方法
handleLogin() {
this.login({ username: 'test', password: '123456' })
.then(() => {
uni.showToast({ title: '登录成功' })
})
.catch(err => {
uni.showToast({ title: '登录失败', icon: 'none' })
})
}
}
}
2. 快捷访问方式
javascript
export default {
methods: {
someMethod() {
// 使用快捷访问
const userHelpers = this.$storeUtils.createModuleHelpers('user')
// 获取状态
const token = userHelpers.state('token')
// 获取getter
const userName = userHelpers.getter('getUserName')
// 提交mutation
userHelpers.commit('SET_USER_INFO', { name: '张三' })
// 分发action
userHelpers.dispatch('logout').then(() => {
console.log('登出成功')
})
}
}
}
3. 在JS文件中使用
javascript
import store from '@/store'
// 直接使用store
const token = store.state.user.token
// 提交mutation
store.commit('user/SET_TOKEN', 'new-token')
// 分发action
store.dispatch('user/login', { username: 'test', password: '123456' })
.then(() => {
console.log('登录成功')
})
最佳实践建议
-
命名规范:
-
模块名使用小驼峰命名法 (user, shoppingCart)
-
state 属性使用小驼峰命名法
-
mutations 使用大写蛇形命名法 (SET_USER_INFO)
-
actions 使用小驼峰命名法
-
-
文档注释 :
在每个模块文件顶部添加注释说明模块用途,重要的 state、getter、mutation 和 action 添加注释
-
模块拆分:
-
按业务功能拆分模块
-
避免单个模块过大
-