✨ Vuex 状态映射与模块化最佳实践
This commit is contained in:
parent
b878032303
commit
b397e00b36
|
@ -5,7 +5,8 @@
|
||||||
<Demo2 />
|
<Demo2 />
|
||||||
<hr />
|
<hr />
|
||||||
<Demo3 /> -->
|
<Demo3 /> -->
|
||||||
<Demo4 />
|
<!-- <Demo4 /> -->
|
||||||
|
<Demo5 />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -13,11 +14,12 @@
|
||||||
// import Demo1 from "@/views/demo1/index.vue";
|
// import Demo1 from "@/views/demo1/index.vue";
|
||||||
// import Demo2 from "@/views/demo2/index.vue";
|
// import Demo2 from "@/views/demo2/index.vue";
|
||||||
// import Demo3 from "@/views/demo3/index.vue";
|
// import Demo3 from "@/views/demo3/index.vue";
|
||||||
import Demo4 from "@/views/demo4/index.vue";
|
// import Demo4 from "@/views/demo4/index.vue";
|
||||||
|
import Demo5 from "@/views/demo5/index.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
// components: { Demo1, Demo2, Demo3 },
|
// components: { Demo1, Demo2, Demo3 },
|
||||||
components: { Demo4 },
|
components: { Demo5 },
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import Vuex from "vuex";
|
import Vuex from "vuex";
|
||||||
import count from "./modules/count";
|
import count from "./modules/count";
|
||||||
|
import schoolInfo from "./modules/schoolInfo";
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
state: {},
|
state: {},
|
||||||
mutations: {},
|
mutations: {},
|
||||||
actions: {},
|
actions: {},
|
||||||
modules: { count },
|
modules: { count, schoolInfo },
|
||||||
});
|
});
|
||||||
|
|
||||||
export default store;
|
export default store;
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
const schoolInfo = {
|
||||||
|
namespaced: true,
|
||||||
|
state: {
|
||||||
|
schoolName: "BunnySchool",
|
||||||
|
schoolAddress: "昆山市印象欧洲",
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
getSchoolName(state) {
|
||||||
|
return state.schoolName + "---";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {},
|
||||||
|
mutations: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default schoolInfo;
|
|
@ -0,0 +1,199 @@
|
||||||
|
# Vuex 状态映射与模块化最佳实践
|
||||||
|
|
||||||
|
## 一、状态访问方式对比
|
||||||
|
|
||||||
|
### 1. 直接访问方式
|
||||||
|
```javascript
|
||||||
|
// 原始访问方式(不推荐)
|
||||||
|
this.$store.state.schoolInfo.schoolName
|
||||||
|
this.$store.getters['schoolInfo/getSchoolName']
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 映射辅助函数
|
||||||
|
```javascript
|
||||||
|
// 推荐使用mapState/mapGetters
|
||||||
|
import { mapState, mapGetters } from 'vuex'
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
// 数组写法(同名映射)
|
||||||
|
...mapState('schoolInfo', ['schoolName']),
|
||||||
|
...mapGetters('schoolInfo', ['getSchoolName']),
|
||||||
|
|
||||||
|
// 对象写法(重命名)
|
||||||
|
...mapState('schoolInfo', {
|
||||||
|
mySchoolName: 'schoolName'
|
||||||
|
}),
|
||||||
|
...mapGetters('schoolInfo', {
|
||||||
|
formattedName: 'getSchoolName'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 二、模块化配置详解
|
||||||
|
|
||||||
|
### 1. 模块定义规范
|
||||||
|
```javascript
|
||||||
|
const schoolInfo = {
|
||||||
|
namespaced: true, // 必须开启命名空间
|
||||||
|
state: () => ({ // 使用函数返回状态对象
|
||||||
|
schoolName: "BunnySchool",
|
||||||
|
schoolAddress: "昆山市印象欧洲"
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
getSchoolName(state) {
|
||||||
|
// 可组合其他getters
|
||||||
|
return `${state.schoolName}---${state.schoolAddress}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
// 使用常量类型
|
||||||
|
UPDATE_NAME(state, payload) {
|
||||||
|
state.schoolName = payload
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
async fetchSchoolInfo({ commit }) {
|
||||||
|
const res = await api.getSchoolInfo()
|
||||||
|
commit('UPDATE_NAME', res.data.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 模块注册方式
|
||||||
|
```javascript
|
||||||
|
// store/index.js
|
||||||
|
import schoolInfo from './modules/schoolInfo'
|
||||||
|
|
||||||
|
export default new Vuex.Store({
|
||||||
|
modules: {
|
||||||
|
schoolInfo // 键名将作为命名空间前缀
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## 三、最佳实践指南
|
||||||
|
|
||||||
|
### 1. 命名规范建议
|
||||||
|
| 类型 | 命名规则 | 示例 |
|
||||||
|
| -------- | ----------- | ----------------- |
|
||||||
|
| 模块文件 | kebab-case | `school-info.js` |
|
||||||
|
| state | camelCase | `studentCount` |
|
||||||
|
| mutation | 大写+下划线 | `UPDATE_INFO` |
|
||||||
|
| getter | 动词短语 | `getFilteredList` |
|
||||||
|
| action | 业务动作 | `fetchSchoolData` |
|
||||||
|
|
||||||
|
### 2. 组件中使用建议
|
||||||
|
```javascript
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
// 优先使用映射辅助函数
|
||||||
|
...mapState('schoolInfo', ['schoolName']),
|
||||||
|
|
||||||
|
// 复杂计算使用本地计算属性
|
||||||
|
formattedAddress() {
|
||||||
|
return this.$store.state.schoolInfo.schoolAddress.replace('市', '')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapMutations('schoolInfo', ['UPDATE_NAME']),
|
||||||
|
|
||||||
|
// 组合多个action
|
||||||
|
async refreshData() {
|
||||||
|
await this.$store.dispatch('schoolInfo/fetchSchoolInfo')
|
||||||
|
this.loadComplete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 代码组织技巧
|
||||||
|
```
|
||||||
|
store/
|
||||||
|
├── index.js # 主入口
|
||||||
|
├── modules/
|
||||||
|
│ ├── school-info.js # 学校模块
|
||||||
|
│ └── user.js # 用户模块
|
||||||
|
└── types.js # mutation类型常量
|
||||||
|
```
|
||||||
|
|
||||||
|
## 四、高级应用场景
|
||||||
|
|
||||||
|
### 1. 动态模块注册
|
||||||
|
```javascript
|
||||||
|
// 按需加载模块
|
||||||
|
export default {
|
||||||
|
created() {
|
||||||
|
import('./store/modules/school-info').then(module => {
|
||||||
|
this.$store.registerModule('schoolInfo', module.default)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.$store.unregisterModule('schoolInfo')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 模块复用
|
||||||
|
```javascript
|
||||||
|
// 创建可复用模块工厂
|
||||||
|
function createSchoolModule(initialName) {
|
||||||
|
return {
|
||||||
|
namespaced: true,
|
||||||
|
state: () => ({
|
||||||
|
schoolName: initialName
|
||||||
|
}),
|
||||||
|
// ...其他配置
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 插件开发
|
||||||
|
```javascript
|
||||||
|
// 状态持久化插件
|
||||||
|
const persistPlugin = store => {
|
||||||
|
// 初始化时读取
|
||||||
|
const savedState = localStorage.getItem('vuex_state')
|
||||||
|
if (savedState) {
|
||||||
|
store.replaceState(JSON.parse(savedState))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅mutation变化
|
||||||
|
store.subscribe((mutation, state) => {
|
||||||
|
localStorage.setItem('vuex_state', JSON.stringify(state))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 五、常见问题解决方案
|
||||||
|
|
||||||
|
1. **模块热更新**:
|
||||||
|
```javascript
|
||||||
|
if (module.hot) {
|
||||||
|
module.hot.accept(['./modules/school-info'], () => {
|
||||||
|
store.hotUpdate({
|
||||||
|
modules: {
|
||||||
|
schoolInfo: require('./modules/school-info').default
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **循环依赖处理**:
|
||||||
|
- 使用全局getter解决跨模块访问
|
||||||
|
- 通过rootState参数访问根状态
|
||||||
|
|
||||||
|
3. **TypeScript支持**:
|
||||||
|
```typescript
|
||||||
|
// 定义模块类型
|
||||||
|
interface SchoolState {
|
||||||
|
schoolName: string
|
||||||
|
schoolAddress: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const schoolInfo: Module<SchoolState, RootState> = {
|
||||||
|
namespaced: true,
|
||||||
|
state: () => ({ /* 初始状态 */ })
|
||||||
|
}
|
||||||
|
```
|
|
@ -0,0 +1,30 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h3>{{ $store.state.schoolInfo.schoolName }}</h3>
|
||||||
|
<h3>{{ schoolName }}</h3>
|
||||||
|
<h3>{{ $store.getters["schoolInfo/getSchoolName"] }}</h3>
|
||||||
|
<h3>{{ getSchoolName }}</h3>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState, mapGetters } from "vuex";
|
||||||
|
export default {
|
||||||
|
name: "Demo-5",
|
||||||
|
data() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// mapState和mapGetters对象写法
|
||||||
|
// ...mapState("schoolInfo", { schoolName: "schoolName" }),
|
||||||
|
|
||||||
|
// mapState和mapGetters数组写法
|
||||||
|
...mapState("schoolInfo", ["schoolName"]),
|
||||||
|
...mapGetters("schoolInfo", ["getSchoolName"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const data = mapState("schoolInfo", { schoolName: "schoolName" });
|
||||||
|
console.log(data);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
Loading…
Reference in New Issue