#Vuex
一、介绍
1.1含义
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
1.2 核心概念
- 单一状态树
Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
单一状态树的优势:
- 如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难;
- 所以Vuex也使用了单一状态树来管理应用层级的全部状态;
- 单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护;
注意:
使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。
- 应用
每一个 Vuex 应用的核心就是 store,里面又包括:
(1)state(数据):用来存放数据源,就是公共状态;
(2)getters(数据加工):有的时候需要对数据源进行加工,返回需要的数据;
(3)actions(事件):要执行的操作,可以进行同步或者异步事件
(4)mutations(执行):操作结束之后,actions通过commit更新state数据源
(5)modules:使用单一状态树,致使应用的全部状态集中到一个很大的对象,所以把每个模块的局部状态分装使每一个模块拥有本身的 state、mutation、action、getters、甚至是嵌套子模块
1.3 工作流程
(1)通过dispatch去提交一个actions,
(2) actions接收到这个事件之后,在actions中可以执行一些异步|同步操作,根据不同的情况去分发给不同的mutations,
(3)actions通过commit去触发mutations,
(4)mutations去更新state数据,state更新之后,就会通知vue进行渲染。
二、Vuex的使用
使用的软件为 Visual Studio Code ,并且已经安装好 node.js,yarn。本文不提供安装方法。如需安装,移步。
- 安装vuex
yarn add vuex
- 在src目录下创建一个store目录,在该目录下创建index.js文件,用于创建Store对象
//用于创建Vuex的核心对象Store
//1.导入
import {createStore } from 'vuex'
//2.创建Store对象
const store = createStore({
//state 用于存储数据
state(){
return {
count:1,
}
},
//actions 用于响应组件中的事件
actions:{
},
//mutations 用于操作数据
mutations:{
}
});
//3.暴露出store对象
export default store;
-
在main.js 使用store对象
import store from './store'; app.use(store);
-
在 Vue 组件中, 可以通过
this.$store
访问store实例 (选项式)<template> <h2>{{$store.state.count}}</h2> </template> <script> export default { name: 'App', data() { return { }; }, components: { }, methods: { }, mounted() { console.log(this.$store.state.count); } }; </script> <style lang="css" scoped> </style>
- 在 Vue 组件中, 可以通过useStore函数来使用store(组合式)
<template> <h2>{{ count }}</h2> </template> <script setup> import { computed } from 'vue'; import { useStore } from 'vuex'; const store = useStore(); const count = computed(() => store.state.count); </script> <style lang="css" scoped> </style>
三、辅助函数
3.1 mapState辅助函数
computed:mapState({
//箭头函数可使代码更简练
name: state => state.name,
// 传字符串参数 'name' 等同于 `state => state.name`
//给name定义一个别名
xingming: 'name',
//扩展函数,可以对name进行操作
namePlusLocalState (state) {
return this.hello + state.name;
},
sex:state=>state.sex,
age:state=>state.age,
}),
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState
传一个字符串数组
computed:mapState(["name","sex","age"]),
mapState
函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed
属性。但是自从有了对象展开运算符,我们可以极大地简化写法
x 1//对象展开运算符2computed:{3 //局部计算属性4 localComputed () { /* ... */ },5 ...mapState(["name","sex","age"]),6},
3.2 mapGetters辅助函数
mapGetters
辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:
<template>
<div>
<h2>总价:{{totalPrice}}</h2>
<h2>书籍信息:{{getBookById(3)}}</h2>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'Book',
computed:{
...mapGetters(['totalPrice','getBookById']),
},
};
</script>
<style lang="scss" scoped>
</style>
3.3 mapMutations的辅助函数
<template>
<div class="box">
<h2>和:{{$store.state.sum}} --{{count}}</h2>
<input type="number" length="3" v-model="num"/>
<input type="button" value="+" @click="add({value:num})"/>
<input type="button" value="-" @click="sub({value:num})"/>
<Counter/>
</div>
</template>
<script>
import {mapMutations } from 'vuex'
export default {
name: 'CalcSum',
data() {
return {
num:1,
};
},
mounted() {
},
methods: {
/*
add(){
//通过dispatch去提交一个action
this.$store.dispatch('increment', this.num);
console.log(this.$store.state.sum)
//强制刷新页面
//this.$forceUpdate();
},
sub(){
this.$store.dispatch('decrement', this.num);
//this.$forceUpdate();
},*/
...mapMutations({
add:'INCREMENT',
sub:'DECREMENT'
}),
},
computed: {
count () {
return this.$store.state.sum
}
}
};
</script>
<style lang="css" scoped>
.box{
width:70%;
margin: auto;
}
</style>
如果组件的事件函数名与Mutation中修改状态的函数名一样,我们可以简写:
...mapMutations(['INCREMENT','DECREMENT']),
mutation重要原则:
一条重要的原则就是要记住 mutation 必须是同步函数,这是因为devtool工具会记录mutation的日记;每一条mutation被记录,devtools都需要捕捉到前一状态和后一状态的快照;但是在mutation中执行异步操作,就无法追踪到数据的变化;
3.4 actions的辅助函数
action也有对应的辅助函数:使用 mapActions
辅助函数将组件的 methods 映射为 store.dispatch
调用(需要先在根节点注入 store
)
四、vuex持久化
4.1 使用持久化原因
Vue项目中使用Vuex作为状态管理,相当于全局的变量存储,可以在所有的vue组件中共享数据、动态修改其状态。vuex是单向数据流,存在vuex中的变量都是响应式数据,组件中一般会通过computed来使用store中的状态、且有缓存
但是当我们去刷新浏览器的时候,store中的状态都会被清空、重新初始化为最初的状态,在某些情况下,我们可能需要将这些状态保存下来,如何让vuex或pinia的状态持久化存储?
要实现持久化存储,简单来说就是将其状态保存到localStorage或者sessionStorage中 .然后在给每个状态默认值的时候就从localStorage或sessionStorage中取就可以了也就是咱们直接手写实现,另外一种方式就是使用第三方插件(vuex-persistedstate或者vuex-persist)
4.2 vuex-persistedstate的使用
-
安装vuex-persistedstate
#npm 安装 npm install vuex-persistedstate #yarn 安装 yarn add vuex-persistedstate
-
在vuex的配置文件store/index.js进行配置:
//1.导入 import {createStore } from 'vuex' import createPersistedState from "vuex-persistedstate"; const store = createStore({ state(){ return { count:0 } }, getters:{ }, actions:{ add(context,value){ console.log(value); setTimeout(() => { context.commit("setCount",1) }, 1000); }, sub(context,value){ context.commit("setCount",-1) } }, mutations:{ setCount(store,value){ store.count += value } }, plugins:[ createPersistedState({ // 存储方式:localStorage、sessionStorage、cookies storage: window.sessionStorage, // 存储的 key 的key值 key: "store", reducer(state) { //render错误修改 // 要存储的数据:本项目采用es6扩展运算符的方式存储了state中所有的数据 return { ...state }; } }) ] }); export default store;
上面是将所有的store中的state状态都持久化存储了。
如果是想只持久化某一个模块的数据,则将上面plugins修改为下面写法
plugins:[ createPersistedState({ // 存储方式:localStorage、sessionStorage、cookies storage: window.sessionStorage, // 存储的 key 的key值 key: "store", // reducer(state) { //render错误修改 // // 要存储的数据:本项目采用es6扩展运算符的方式存储了state中所有的数据 // return { ...state }; // } // 只持久化存储user模块的状态 paths: ['user'] }) ]