SpringBootVue(十三)VueX状态管理库
对于组件化开发来说,大型应用的状态往往跨越多个组件。在多层嵌套的父子组件之间传递状态十分麻烦,而Vue更是没有为兄弟组件提供直接共享数据的方法。
基于这个问题,许多框架提供了解决方案:使用全局的状态管理器,将所有分散的共享数据交由状态管理器保管,Vue也不例外。
VueX 是专为Vue.js 应用程序开发的状态管理库,采用集中式存储来管理应用的所有组件状态。
VueX 用于管理分散在Vue各个组件中的数据。兄弟组件间的数据传递。
vuex有两个版本,分别vuex3对应vue2 ; vuex4对应vue3
两种版本安装方式:
vuex3安装:npm install vuex@3
vuex4安装:npm install vuex
官方网站: https://v3.vuex.vuejs.org/zh/
关于状态管理:
一般在复杂应用中才会用到VueX ,并非必须的。
每一个VueX应用的核心都是一个store (仓库) 全局对象,与普通的全局对象不同,基于Vue数据与视图绑定的特点,当store中的状态(数据)发生变化时,与之绑定的视图也会被重新渲染。
store中的状态不允许被直接修改,改变store中的状态的唯一途径就是显式提交(commit)mutation,这可以方便地跟踪每一个状态的变化。
在大型复杂应用中,如果无法有效跟踪到状态的变化,将会对理解和维护代码带来极大困扰
VueX中有5个重要概念:State、Getter、Mutation、Action、Module
下面进行演示
创建一个基于 Vue2 的新项目:具体创建方式详见笔记(九)
vue create vuex-demo
将刚刚创建的项目 vuex-demo 拖入 Visual Studio Code 中
vuex3安装:npm install vuex@3
在src目录下新建一个 store 文件夹,新建一个 index.js 文件,提供一个初始 state 对象和一些 mutation ,文件代码如下:import Vue from "vue" import Vuex from "vuex" Vue.use(Vuex) const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } })
说明:建立了一个store(仓库),默认count初始值为: 0 ;以后state 是状态就是给各个组件调用的。
不能直接改state的状态,如需要修改要调用 mutations 方法(该方法是用于 +1 操作)。
然后将store 导出,这样就可以在main.js中使用了export default store
现在希望在组件中调用 count 的值,具体操作如下:
1.由于我们是在main.js创建Vue的,因此在main.js 中就可以导入刚刚创建的 index.jsimport store from "./store/index.js"
由于是以index.js作为文件名,也可以简写省略import store from "./store"
然后用Vuex 提供的一个从根组件向所有子组件,以 store 选项的方式"注入"该 store 的机制:new Vue({ render: h => h(App), store:store }).$mount("#app") 做完这些就可以在任意组件中利用store对象去取值。 2.新建一个Test.vue 组件,代码如下:
在根组件App.vue中进行注册
在这个Test.vue 中将 count 的值取出来。{{ this.$store.state.count }}
运行项目后,浏览器就可以显示出 count 的初始值 :"0"。这个时候Test.vue 组件就成功调用了全局状态值。
如何修改状态码?增加一个按钮,进行加1操作。
定义add方法:methods:{ add(){ this.$store.commit("increment") } }
再次强调,我们通过提交 mutation 的方式,而非直接改变 store.state.count,是因为我们想要更明确地追踪到状态的变化。
这里调用的是index.js 中 store里面的 increment 方法来进行修改操作,而不是用 this.$store.state.count = this.$store.state.count +1
在浏览器中点击 +1 按钮就可以改变状态了
优化1:可以通过设置一个方法,然后就可以直接调用 {{ count }}computed: { count () { return this.$store.state.count } },
优化2:mapState 辅助函数生成计算属性
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。// 在单独构建的版本中辅助函数为 Vuex.mapState import { mapState } from "vuex" computed: mapState([ // 映射 this.count 为 store.state.count "count" ])
Getter
有时候我们需要从 store 中的 state 中派生出一些状态,例如对state 数据进行过滤并进行计数等处理。
下面进行演示,在state 中再增加一个 todos 待办事项列表。其中,吃饭已经完成;睡觉尚未完成。 todos: [ { id: 1, text: "吃饭", done: true }, { id: 2, text: ".睡觉", done: false } ]
假设要显示到 Test.vue 组件上,现将 todos 映射过来,然后就可以使用。
现在假设只是想显示已经完成的动作,该怎样操作?
这就需要对 todos 进行过滤。在 index.js 中增加一个 getters 方法(注意:两个方法之间要加逗号 " , ") getters: { doneTodos: state => { return state.todos.filter(todo => todo.done) } }
然后就可以在Test.vue 组件中使用 mapGetters 辅助函数来映射。
由于我们原来已经使用了 mapState ,现在又要同时用另外一个方法 mapGetters 就需要对代码进行一些修改: computed:{ ...mapState([ "count","todos" ]), ...mapGetters([ "doneTodos" ]) },
这时浏览器就过滤出 done 状态为 true 的数据
下面的Mutation、Action、Module 后面的实例讲解中才能更好理解,在此仅作为了解。 Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
组件中提交 Mutation的方法,代码如下。不再此进行演示,有兴趣可以自己试一下。import { mapMutations } from "vuex" export default { // ... methods: { ...mapMutations([ "increment", // 将 `this.increment()` 映射为 `this.$store.commit("increment")` // `mapMutations` 也支持载荷: "incrementBy" // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit("incrementBy", amount)` ]), ...mapMutations({ add: "increment" // 将 `this.add()` 映射为 `this.$store.commit("increment")` }) }}
Action
在 mutation 中处理混合异步调用时就要用到 Action 。( 在 Vuex 中, mutation 都是同步事务 : )
先注册一个简单的 action: const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit("increment") } }})
在组件中分发 Action ,与前面类似:methods 中使用 mapActionsimport { mapActions } from "vuex" export default { // ... methods: { ...mapActions([ "increment", // 将 `this.increment()` 映射为 `this.$store.dispatch("increment")` // `mapActions` 也支持载荷: "incrementBy" // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch("incrementBy", amount)` ]), ...mapActions({ add: "increment" // 将 `this.add()` 映射为 `this.$store.dispatch("increment")` }) }} Modules
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成 模块(module) 。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。 const moduleA = { state: () => ({ ... }), mutations: { ... }, actions: { ... }, getters: { ... }} const moduleB = { state: () => ({ ... }), mutations: { ... }, actions: { ... }} const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB }}) store.state.a // -> moduleA 的状态 store.state.b // -> moduleB 的状态
最终所有模块都要和到一起。当我们创建 Vuex.Store 时,通过 modules 参数给模块起名字,比如:给 moduleA 取名为 a 。
需要访问的模块的时候就加上 a 。store.state.a // -> moduleA 的状态。
其实就是对模块进行分割管理,比如:用户模块的状态,订单模块的状态等分别设置。
上述仅为 Vuex 的基本概念,具体使用到后面的综合案例再具体演示。