一、基础
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]
});
本文转载自微信公众号「前端点线面」,可以通过以下二维码关注。转载本文请联系前端点线面公众号。