一、基础
Vuex是一个专为Vue.js应用程序开发的状态管理模式,采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化,每一个Vuex应用的核心是store(仓库)。为了使用Vuex,首先需要安装该包,执行如下命令完成该安装过程。
npm install vuex@next --save
二、核心概念
Vuex中核心概念主要有五个:State、Getters、Mutations、Actions、Modules,每一部分都有自己的功用,下面先来看一段简单的代码,该代码中涉及到了这些核心概念,然后在该代码的基础上进一步理解。
// index.js文件 import {createStore} from "vuex"; import {moduleA} from "./module/moduleA"; export const store = createStore({ // Vuex允许将store分割成模块(module),每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块 // 访问moduleA的状态:store.state.moduleA modules: { moduleA } }); // module/moduleA.js文件 // 对于模块内部的mutation和getter,接收的第一个参数是模块的局部状态对象 // 对于模块内部的action,局部状态通过context.state暴露出来,根节点状态则为context.rootState // 对于模块内部的getter,根节点状态会作为第三个参数暴露出来 // 在带命名空间的模块内访问全局内容 // 如果希望使用全局state和getter,rootState和rootGetters会作为第三和第四个参数传入getter,也会通过context对象的属性传入action // 若需要在全局命名空间内分发action或提交mutation,将{root: true}作为第三个参数传给dispatch或commit即可。 export const moduleA = { // 默认情况下,模块内部的action、mutation和getter是注册在全局命名空间的,如果希望模块具有更高的封装度和复用性,可以通过添加namespaced:true的方式使其成为带命名空间的模块 namespaced: true, state: { testState1: 'xxxx', testState2: { a: 0, b: 1 }, testState3: 0 }, // 有的时候需要从store中的state中派生出一些状态,此时可以将该部分抽象出一个函数供多处使用。 // Vuex允许在store中定义getter,像计算属性一样,getter的返回值会根据它的依赖被缓存起来,且只有当他的依赖值发生了改变才会被重新计算 getters: { // getter接收state作为其第一个参数 testGetter1: state => { return state.testState1 + state.testState3; }, // getter可以接受其他getter作为第二个参数 testGetter2: (state, getters) => { return getters.testGetter1.length; } }, // 更改Vuex的store中的状态的唯一方法是提交mutation,每个mutation都有一个字符串的事件类型和一个回调函数,该回调函数接收state作为第一个参数,提交的载荷作为第二个参数 // 以相应的type调用store.commit方法来触发相应的回调函数 // Mutation必须是同步函数 mutations: { testMutation1(state) { // 变更状态 state.testState3++; }, // 第二个参数是载荷 testMutation2(state, payload) { state.testState1 += payload.content; } }, // Action提交的是mutation,而不是直接变更状态 // Action可以包含任意异步操作 // Action函数接受一个与store实例具有相同方法和属性的context对象,因此可以调用context.commit提交一个mutation,或者通过context.state和context.getters来获取state和getters。 // Action通过store.dispatch方法触发 actions: { testAction1(context) { setTimeout(() => { context.commit('testMutation1'); }, 1000); }, testAction2({commit}, payload) { setTimeout(() => { commit({ type: 'testMutation2', content: payload.content }); }, 1000); } } };
- State
Vuex使用单一状态树保存所有数据,作为唯一数据来源。如上所示代码,其包含了testState1、testState2、testState3,在组件中如何获取这些数据呢?(Vue3.x语法)
<template> <h1>组件三</h1> <p>{{testState1}}</p> <p>{{testState2}}</p> <p>{{testState3}}</p> </template> <script setup> import {computed} from 'vue'; import {useStore} from 'vuex'; // 通过useStore函数来访问store,与选项式API const store = useStore(); // 从store中读取状态并显示到Vue组件中最简单的办法是将其绑定到计算属性上 // 如果想读取某一模块中的状态值,则需要store.state.模块名.状态名 const testState1 = computed(() => store.state.moduleA.testState1); const testState2 = computed(() => store.state.moduleA.testState2.a + store.state.moduleA.testState2.b); const testState3 = computed(() => store.state.moduleA.testState3); </script> <style lang="less"></style>
- Getters
有的时候需要从store中的state中派生出一些状态,此时可以将该部分抽象出一个函数供多处使用。Vuex允许在store中定义getter,像计算属性一样,getter的返回值会根据它的依赖被缓存起来,且只有当他的依赖值发生了改变才会被重新计算。
<template> <h1>组件三</h1> // …… <p>{{testGetter1}}</p> <p>{{testGetter2}}</p> </template> <script setup> import {computed} from 'vue'; import {useStore} from 'vuex'; // 通过useStore函数来访问store,与选项式API const store = useStore(); // …… // 在computed函数中访问getter // 如果访问非模块中的getter,直接store.getters.名称 // 如果访问模块中的getter,通过store.getters['getter路径'] const testGetter1 = computed(() => store.getters['moduleA/testGetter1']); const testGetter2 = computed(() => store.getters['moduleA/testGetter2']); </script> <style lang="less"></style>
- Mutations
更改Vuex的store中的状态的唯一方法是提交mutation,每个mutation都有一个字符串的事件类型和一个回调函数,该回调函数接收state作为第一个参数,提交的载荷作为第二个参数。(注意:Mutation必须是同步函数)
<template> <h1>组件三</h1> // …… <button @click="handleMutationsClick">改变</button> </template> <script setup> // …… // 与mutations相应的type调用store.commit方法来触发相应的回调函数 const handleMutationsClick = () => { store.commit('moduleA/testMutation1'); store.commit('moduleA/testMutation2', { content: 'content' }); }; </script> <style lang="less"></style>
- Actions
Actions类似于mutation,但是仍然有一些不同点,一方面其提交的是mutation,而不是直接变更状态;另一方面Action可以包含任意异步操作。
<template> <h1>组件三</h1> // …… <button @click="handleActionsClick">改变Action</button> </template> <script setup> // …… // Action函数接受一个与store实例具有相同方法和属性的context对象,因此可以调用context.commit提交一个mutation,或者通过context.state和context.getters来获取state和getters。 // Action通过store.dispatch方法触发 const handleActionsClick = () => { store.dispatch('moduleA/testAction1'); store.dispatch('moduleA/testAction2', { content: 'test' }); }; </script> <style lang="less"></style>
- Modules
由于使用单一状态树,应用的所有状态都会集中到一个较大的对象,当应用变的复杂时store会变的很难维护,为了解决该问题,Vuex允许将store分割成模块(module),每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块,具体使用可见第二节中开头部分的代码,其中包含了其常见的使用方式。
三、插件
3.1 插件基础
Vuex在实例化store的时候可以接受plugins选项,该选项可以添加一系列的插件,插件就可以帮助我们完成一系列的工作,节省人力和物力,下面我们自定义一个简单的插件并调用该插件。
// plugins/myPlugin.js // 插件接收唯一的参数store const myPlugin = store => { // store上有一系列的方法,可以用在插件中https://next.vuex.vuejs.org/zh/api/#commit // 注册一个动态模块用registerModule // 替换store的根状态用replaceState // 监听mutation的变化,该处理函数会在每个mutation完成后调用,接收mutation和经过mutation后的状态作为参数 store.subscribe((mutation, state) => { console.log(mutation); }); }; export default myPlugin; // index.js import {createStore} from "vuex"; import myPlugin from "./plugins/myPlugin"; export const store = createStore({ // …… // 一个数组,包含应用在store上的插件方法,这些插件直接接收store作为唯一参数,可以监听mutation或者提交mutation plugins: [myPlugin] });
3.2 数据持久化插件
Vuex的状态存储并不能持久化,只要一刷新页面数据就丢失了,此时可引入vuex-persist插件来解决该问题,其会将状态保存至cookie或者localStorage中。
import {createStore} from "vuex"; import VuexPersistence from "vuex-persist"; import myPlugin from "./plugins/myPlugin"; // 利用该插件可实现对store数据的持久化 const vuexLocal = new VuexPersistence({ storage: window.localStorage }); export const store = createStore({ // …… // 一个数组,包含应用在store上的插件方法,这些插件直接接收store作为唯一参数,可以监听mutation或者提交mutation plugins: [myPlugin, vuexLocal.plugin] });