[TOC] #### 1. Vuex 介绍 --- Vuex 官方文档: [https://vuex.vuejs.org/zh](https://vuex.vuejs.org/zh) Vuex 当前最新版为 Vuex 4.x 【当前时间 2022-10】 Vue 3 使用 Vuex 4,而 Vue 2 使用 Vuex 3,本文记录的是 Vuex3 的使用总结,[Vuex 3.x 文档](https://v3.vuex.vuejs.org/zh) **一、组件之间共享数据的方式:** 父向子传值: v-bind 属性绑定,子向父传值: v-on 事件绑定 **二、那么页面之间如何共享数据呢 ?这就需要使用到 vuex 了** vuex 可以实现多个组件中共享状态(数据) 官方解释: Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 换句话说,vuex 是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享 **三、Vuex 术语** 在 vuex 中,`状态`指的是共享的数据,也就是 vuex 的 state 的值 **四、使用 vuex 统一管理状态的好处** a. 高效的实现数据共享,提高开发效率 b. 集中管理共享的数据,易于开发和后期维护 c. 存储在 vuex 中的数据都是响应式的,能够实时保持数据与页面的同步 #### 2. Vuex 安装 --- 将 vuex 安装为 运行时依赖【本文章默认项目环境为 vue 脚手架】 vue 脚手架需要安装该依赖,而 uniapp 项目已经内置 vuex,无需安装即可直接使用 ``` npm install vuex --save ``` 创建文件: **src/store/index.js**, 文件内容如下: ```javascript import Vue from 'vue'; import Vuex from 'vuex'; // 1. 安装插件 Vue.use(Vuex) // 2. 创建对象 const store = new Vuex.Store({ }) // 3. 导入 store 对象 export default store ``` 在 main.js 中导入 store 对象,并将 store 对象挂载到 vue 实例上 ```javascript import store from './store' new Vue({ el: '#app', store, render: h => h(App) }) ``` #### 3. Vue.js devtools 插件 --- 多个界面修改 vuex 状态时,这个工具会对状态进行跟踪,当出现问题时,可以更好的调试错误  #### 4. state 数据的访问方式 --- **方法一:通过 vue 实例访问** 因为将 vuex 挂载到了 vue 实例中,所以 vuex 的数据可以通过 vue 实例访问 ```javascript this.$store.state ``` **方法二:使用 mapState 函数将 vuex 数据映射为计算属性** ```javascript // 1. 从 vuex 中按需导入 mapState 函数 import { mapState } from 'vuex'; // 2. 使用 mapState 函数将 vuex 数据映射为当前组件的计算属性 (computed) export default { computed: { ...mapState(['token', 'userInfo']) } } // 也可以这样写 export default { computed: mapState(['token', 'userInfo']) } ``` #### 5. getters 的使用详解 --- 使用场景: 当某一个数据需要经过一系列的操作后再返回时,可以使用 getters 处理 Getter 用于对 Store 中的数据进行加工处理形成新的数据,类似 Vue 的计算属性(computed),起到一个包装器的作用,当 Store 中的数据发生变化时, Getter 的数据也会跟着变化 getters 方法定义: ```javascript const store = new Vuex.Store({ state: { counter: 6 }, getters: { // 返回值是一个数据 power(state) { return state.counter * state.counter }, // 返回值是一个函数 add(state) { return (num) => { return state.counter + num } } } }) ``` **调用 getters 的两种方式** 方式一、使用 `this.$store.getters.名称` 调用 getters ``` // 调用方式 this.$store.getters.power // 在组件模板中调用 {{ $store.getters.power }} ``` getters 的其他用法参考下方代码示例: ``` {{ $store.getters.more20Stu }} {{ $store.getters.more20StuCount }} {{ $store.getters.moreAgeStu(20) }} ``` ```javascript const store = new Vuex.Store({ state: { students: [ { id: 1, name: "wang", age: 18 }, { id: 2, name: "liang", age: 21 }, { id: 3, name: "zhang", age: 30 }, ], }, getters: { more20Stu(state) { // 获取年龄大于20的 return state.students.filter(s => s.age > 20) }, more20StuCount(state, getters) { // 获取年龄大于20的个数 return getters.more20Stu.length }, moreAgeStu(state) { // 如果要传参数,需要返回一个函数 return age => { return state.students.filter(s => s.age > age) } } } }) ``` 方式二、使用 mapGetters 将 getters 方法映射为当前组件的计算属性 ```javascript // 1. 从 vuex 中按需导入 mapGetters 函数 import { mapGetters } from 'vuex'; // 2. 使用 mapGetters 函数将 getters 方法映射为当前组件的计算属性 (computed) export default { computed: { ...mapGetters(['power', 'total']) } } ``` #### 6. mutations 的使用详解 --- vuex 的 state 数据更新的唯一方式: 提交 Mutation mutations 用于变更 store 中的数据。只能通过 mutations 变更 store 数据,不能直接操作 store 数据,这种方式虽然操作起来繁琐一些,但是可以集中监控所有数据的变化 mutation 主要包括两部分: 事件类型 (type)、回调函数 (handler) mutation 的定义方式: a. increment 称为事件类型,回调函数的第一个参数永远就是 state b. mutations 中的方法第二个参数被称为 mutations 的载荷 (payload) ```javascript { state: { counter: 1 }, mutations: { mutations: { increment(state) { state.counter++ }, decrement(state, num) { state.counter -= num }, } } } ``` **触发 mutation 的两种方式** 方式一、使用 `this.$store.commit` 触发 mutations ```javascript // commit 的作用就是调用某个 mutation 函数 this.$store.commit("increment");//不需要参数 this.$store.commit("decrement", num);//传参 ``` 通过 commit 进行提交是一种普通的提交方式,vue 还提供了另外一种风格,它是一个包含 type 属性的对象 ```javascript this.$store.commit({ type: "add", num: 10, age: 20, }); ``` 此时要注意 mutation 中的方法的第二个参数的值,和普通提交方式可不一样 ```javascript add(state, payload) { console.log(payload) // {type: 'add', num: 10, age: 20} } ``` 方式二、使用 `mapMutations` 将 mutations 函数映射为当前组件的 methods 方法 ```javascript // 1. 从 vuex 中按需导入 mapMutations 函数 import { mapMutations, } from 'vuex'; // 2. 使用 mapMutations 将 mutations 函数映射 methods 方法 export default { methods: { ...mapMutations(['login', 'getUserInfo']), } } ``` #### 7. actions 的使用详解 ---- vuex 要求 mutations 中的方法体内容必须是同步操作 主要的原因是当我们使用 devtools 插件时,devtools 可以帮助我们捕捉 mutation 的快照,但如果是异步操作,那么 devtools 将不能很好的追踪这个操作什么时候完成。 异步操作必须写在 actions 中,通过触发 mutations 的方式间接变更数据 代码示例: ```javascript const store = new Vuex.Store({ state: { counter: 6, }, mutations: { change(state, num = 1) { state.counter += num } }, actions: { update(context) { setTimeout(() => { context.commit('change') }, 1000) } } }) ``` **触发 actions 的两种方式** 方式一、使用 `this.$store.dispatch` 触发 actions ```javascript // 不传参数 this.$store.dispatch("update"); // 传递参数: dispatch 的第二个参数 this.$store.dispatch("update", 10); // actions 方法在 commit 时也要将参数带过去( payload 可以设置默认值) update(context, payload = 1) { setTimeout(() => { context.commit('change', payload) }, } } ``` 因为 actions 方法都是异步操作,所以当异步操作结束后应该通知外面我已操作完成 ```javascript this.$store.dispatch("update", () => { console.log("异步操作结束"); }); actions: { update(context, payload) { setTimeout(() => { context.commit('change') payload() }, 1000) } } ``` 当需要传入参数时,可以这样写 ```javascript this.$store.dispatch("update", { payload: { name: "liang", age: 23, }, success: () => { console.log("异步操作结束"); }, }); actions: { update(context, params) { console.log(params.payload) setTimeout(() => { context.commit('change') params.success() }, 1000) } }, ``` 更优雅的写法 Promise: ```javascript this.$store .dispatch("update", { name: "liang", age: 23, }) .then((result) => { console.log("result: ", result); }); actions: { update(context, payload) { return new Promise((resolve, reject) => { console.log(payload) setTimeout(() => { context.commit('change') resolve('异步操作结束') }, 1000) }); } } ``` 方式二、使用 `mapActions` 将 actions 函数映射为当前组件的 methods 方法 ```javascript // 1. 从 vuex 中按需导入 mapActions 函数 import { mapActions } from 'vuex'; // 2. 将指定的 actions 函数,映射为当前组件的 methods 方法 export default { methods: { ...mapActions(['save', 'update']), } } ``` #### 8. modules 的使用详解 --- vue 使用单一状态树,那么也就意味着很多状态都会交给 vuex 管理,当应用变得非常复杂时,store 对象就有可能变得相当臃肿,为了解决这个问题,vuex 允许我们将 store 分割成模块,每个模块拥有自己的 state、mutations、actions、getters 等 ``` // state {{ $store.state.a.name }} // getters {{ $store.getters.name }} {{ $store.getters.fullname }} // mutations this.$store.commit("updateName"); // actions this.$store.dispatch("save"); ``` ```javascript const store = new Vuex.Store({ state: { counter: 6, }, modules: { a: { state: { name: 'liang' }, mutations: { updateName(state) { state.name = 'zhang' } }, getters: { name(state) { return state.name + ' 666' }, // 在模块的 getters 中有第三个参数 fullname(state, getters, rootState) { return getters.name + ' ' + rootState.counter } }, actions: { save(context) { setTimeout(() => { context.commit('updateName') }, 1000); } } } } }) ``` #### 9. store 文件夹的目录组织 --- 当我们的 vuex 帮助我们管理过多内容时,好的项目结构可以让我们的代码更加清晰 官方文档也给出了目录结构示例: [https://vuex.vuejs.org/zh/guide/structure.html](https://vuex.vuejs.org/zh/guide/structure.html) #### 10. vuex 用法总结 --- 获取 state 数据的两种方式 ```javascript // 方式一、使用 this.$store.state.名称 this.$store.state // 方式二、使用 mapState 将 state 数据映射为当前组件的计算属性 // 1. 从 vuex 中按需导入 mapState 函数 import { mapState } from 'vuex'; // 2. 使用 mapState 函数将 vuex 数据映射为当前组件的计算属性 (computed) export default { computed: { ...mapState(['token', 'userInfo']) } } ``` 调用 getters 方法的两种方式 ```javascript // 方式一、使用 this.$store.getters.名称 this.$store.getters.power // 方式二、使用 mapGetters 将 getters 方法映射为当前组件的计算属性 // 1. 从 vuex 中按需导入 mapGetters 函数 import { mapGetters } from 'vuex'; // 2. 使用 mapGetters 函数将 getters 方法映射为当前组件的计算属性 (computed) export default { computed: { ...mapGetters(['power', 'total']) } } ``` 触发 mutations 方法的两种方式 ```javascript // 方式一、使用 this.$store.commit 触发 mutations this.$store.commit("increment") this.$store.commit("decrement", num) // 方式二、使用 mapMutations 将 mutations 函数映射为当前组件的 methods 方法 // 1. 从 vuex 中按需导入 mapMutations 函数 import { mapMutations, } from 'vuex'; // 2. 使用 mapMutations 将 mutations 函数映射 methods 方法 export default { methods: { ...mapMutations(['login', 'getUserInfo']), } } ``` 触发 actions 方法的两种方式 ```javascript // 方式一、使用 this.$store.dispatch 触发 actions this.$store.dispatch("update") this.$store.dispatch("update", payload) // 方式二、使用 mapActions 将 actions 函数映射为当前组件的 methods 方法 // 1. 从 vuex 中按需导入 mapActions 函数 import { mapActions } from 'vuex'; // 2. 将指定的 actions 函数,映射为当前组件的 methods 方法 export default { methods: { ...mapActions(['save', 'update']), } } ```