Compare commits
2 Commits
b878032303
...
39b773f409
Author | SHA1 | Date |
---|---|---|
|
39b773f409 | |
|
b397e00b36 |
|
@ -10,6 +10,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
|
"vue-router": "3",
|
||||||
"vuex": "3"
|
"vuex": "3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -14,6 +14,9 @@ importers:
|
||||||
vue:
|
vue:
|
||||||
specifier: ^2.6.14
|
specifier: ^2.6.14
|
||||||
version: 2.7.16
|
version: 2.7.16
|
||||||
|
vue-router:
|
||||||
|
specifier: '3'
|
||||||
|
version: 3.6.5(vue@2.7.16)
|
||||||
vuex:
|
vuex:
|
||||||
specifier: '3'
|
specifier: '3'
|
||||||
version: 3.6.2(vue@2.7.16)
|
version: 3.6.2(vue@2.7.16)
|
||||||
|
@ -3575,6 +3578,11 @@ packages:
|
||||||
vue:
|
vue:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
vue-router@3.6.5:
|
||||||
|
resolution: {integrity: sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^2
|
||||||
|
|
||||||
vue-style-loader@4.1.3:
|
vue-style-loader@4.1.3:
|
||||||
resolution: {integrity: sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==}
|
resolution: {integrity: sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==}
|
||||||
|
|
||||||
|
@ -7700,6 +7708,10 @@ snapshots:
|
||||||
'@vue/compiler-sfc': 3.5.16
|
'@vue/compiler-sfc': 3.5.16
|
||||||
vue: 2.7.16
|
vue: 2.7.16
|
||||||
|
|
||||||
|
vue-router@3.6.5(vue@2.7.16):
|
||||||
|
dependencies:
|
||||||
|
vue: 2.7.16
|
||||||
|
|
||||||
vue-style-loader@4.1.3:
|
vue-style-loader@4.1.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
hash-sum: 1.0.2
|
hash-sum: 1.0.2
|
||||||
|
|
|
@ -1,15 +1,35 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="">
|
<html lang="">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
<link
|
||||||
|
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
integrity="sha384-LN+7fdVzj6u52u30Kp6M/trliBMCMKTyK833zpbD+pXdCLuTusPj697FH4R/5mcr"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
|
||||||
|
<script
|
||||||
|
src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
|
||||||
|
integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.min.js"
|
||||||
|
integrity="sha384-7qAoOXltbVP82dhxHAUje59V5r2YsVfBafyUDxEdApLPmcdhBPg1DKg1ERo0BZlK"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
></script>
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
<strong
|
||||||
|
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
|
||||||
|
properly without JavaScript enabled. Please enable it to
|
||||||
|
continue.</strong
|
||||||
|
>
|
||||||
</noscript>
|
</noscript>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<!-- built files will be auto injected -->
|
<!-- built files will be auto injected -->
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
<Demo2 />
|
<Demo2 />
|
||||||
<hr />
|
<hr />
|
||||||
<Demo3 /> -->
|
<Demo3 /> -->
|
||||||
<Demo4 />
|
<!-- <Demo4 /> -->
|
||||||
|
<Demo6 />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -13,11 +14,13 @@
|
||||||
// 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";
|
||||||
|
import Demo6 from "@/views/demo6/index.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
// components: { Demo1, Demo2, Demo3 },
|
// components: { Demo1, Demo2, Demo3 },
|
||||||
components: { Demo4 },
|
components: { Demo6 },
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
|
import VueRouter from "vue-router";
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
|
import router from "./router";
|
||||||
import store from "./store";
|
import store from "./store";
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
|
|
||||||
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
render: (h) => h(App),
|
render: (h) => h(App),
|
||||||
store,
|
store,
|
||||||
|
router,
|
||||||
}).$mount("#app");
|
}).$mount("#app");
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import VueRouter from "vue-router";
|
||||||
|
|
||||||
|
const router = new VueRouter({
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: "/about",
|
||||||
|
component: () => import("@/views/demo6/components/About.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/home",
|
||||||
|
component: () => import("@/views/demo6/components/Home.vue"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
|
@ -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>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>About内容</h2>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "AboutPage",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h3>Home内容</h3>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "HomePage",
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,39 @@
|
||||||
|
<template>
|
||||||
|
<div class="container mt-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-2 col-xs-offset-2 text-center">
|
||||||
|
<!-- 路由跳转内容 -->
|
||||||
|
<div class="list-group">
|
||||||
|
<router-link
|
||||||
|
class="list-group-item"
|
||||||
|
active-class="active"
|
||||||
|
to="/about"
|
||||||
|
>
|
||||||
|
about
|
||||||
|
</router-link>
|
||||||
|
<router-link class="list-group-item" active-class="active" to="/home"
|
||||||
|
>home</router-link
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内容显示区域 -->
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel-body">
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Demo-6",
|
||||||
|
data() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
Loading…
Reference in New Issue