数据持久化
vuex
vuex 是一个状态管理工具,可以管理整个应用的状态,包括数据、状态、配置等。
使用完整步骤
- 安装:npm install vuex --save
- 导入:import Vuex from ‘vuex’
- 创建store:const store = new Vuex.Store({})
- 在main.js中引入store:
el: '#app',
router,
store,
render: h => h(App)
}).$mount('#app')
- 在组件中引入store:import { mapState, mapMutations } from ‘vuex’
- 在computed中引用:
computed: {
...mapState(['count'])
...mapMutations(['increment'])
...mapActions(['incrementAsync'])
...mapGetters(['doubleCount'])
}
vuex组成
state:
提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储,存放状态,类似data,但只读,不能直接修改,只能通过mutation修改
存储
const store = new Vuex.Store({
// state中存放的就是全局共享的数据
state: { count: 0 }
})
读取
this.$store.state.count
或者
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['count'])
}
}
mutations
用来变更Store中的数据
- 只能通过mutation变更Store的数据,不可以直接操作Store中的数据
- 约定mutations中不进行异步操作,基本上是直接对 state 里面数据进行赋值的
为什么需要约定mutations中不进行异步操作?
- 易于调试和追踪
在 mutations 中进行同步操作可以确保每次状态更新都是明确且可追踪的。这使得开发者更容易理解状态的变化过程,也便于调试和测试。如果在 mutations 中引入异步操作,会导致状态更新的时间点变得不确定,增加调试难度。 - 确保状态的一致性
在 mutations 中执行同步操作可以确保在提交状态变更时,状态是一致的。如果在 mutations 中执行异步操作,可能会导致在异步操作完成之前状态就已经被其他操作改变了,从而导致状态不一致的问题。 - 保持代码可预测性
异步操作通常涉及多个步骤,并且在不同的时刻可能多次更改状态。如果在 mutations 中进行异步操作,会导致状态更新的时间点和顺序变得不可预测,从而增加代码的复杂度。 - 分离关注点
将同步操作放在 mutations 中,将异步操作放在 actions 中,这是一种分离关注点的做法。这样的设计让代码更易于理解和维护。mutations 专注于状态的直接变更,而 actions 负责处理更复杂的业务逻辑和异步请求
设置
// 定义Mutation
const store = new Vuex.Sore({
state: { count: 0 },
mutations: {
//mutations可以带多个参数,但第一个参数是state
add(state) {
state.count ++
}
}
})
调用
this.$store.commit('add')
或者
// 1. 从vuex中按需导入mapMutations函数
import { mapMutations } from 'vuex'
// 2. 通过刚才导入的mapMutations函数,将需要的mutations函数,映射为当前组件的methods方法
methods: {
...mapMutations(['add', 'addN'])
}
actions
Action用于处理异步任务
- 如果通过异步操作变更数据,必须通过Action,而不能使用Mutation,但是在Action中还是通过触发Mutation的方式间接变更数据。
配置
const store = new Vuex.Store({
state: {},
mutations: {
add(state) {
state.count ++
}
},
actions: {
//第一个形参context就是new出来的Store对象
addAsync(context /*或者是{commit, state}*/) {
setTimeout({} => {
context.commit('add')
}, 1000)
}
}
})
调用
methods: {
handle() {
this.$store.dispatch('addAsync')
}
}
或者
// 1. 从vuex中按需导入mapActions函数
import { mapActions } from 'vuex'
// 2. 通过mapActions函数,将需要的actions函数,映射为当前组件的methods方法
methods: {
...mapActions(['addAsync', 'addNAsync'])
}
getter
用于对Store中的数据进行加工处理形成新的数据,不会变更state中的数据
- Getter可以对Store中已有的数据加工处理之后欧形成新的数据,类似Vue的计算属性
- Store中数据发生变化,Getter的数据也会跟着变化
配置
const store = new Vuex.Store({
// state中存放的就是全局共享的数据
state: { count: 0 },
getters: {
showNum: state => {
return '当前最新的数量是【' + state.count + '】'
}
}
})
调用
this.$store.getters.名称
import { mapGetters } from 'vuex'
computed: {
...mapGetters(['showNum'])
}
模块化modules
随着应用规模的增长,单一的 Vuex store 可能会变得难以管理和维护。为了应对这个问题,Vuex 支持模块化的 store 设计。通过模块化,可以将状态、mutations、actions 和 getters 分成多个独立的模块,每个模块负责管理特定领域或功能的状态
配置
// 先配置单个模块
const state = {};
const getters = {};
const mutations = {};
const actions = {};
export default {
// 一般在模块使用时,都会加入这个属性。
namespaced: true,
state,
getters,
mutations,
actions
};
//将所有模块引入到store/index.js中
import Vue from "vue";
import vuex from "vuex";
import car from "./modules/car";
import goods from "./modules/goods";
Vue.use(vuex);
export default new vuex.Store({
modules: {
car,
goods
}
});
使用
- 组件内通过:$store.state.模块名.属性名 查看获取Vuex模块化数据、
- mapState通过函数赋值
this.$store.car.info
或者
computed: {
...mapState({
count: state => state.home.count
})
},
methods: {
...mapMutation({
sum: "home/sum"
})
}
命名空间
在Vuex中设置命名空间(namespaced)可以帮助我们更好地组织和管理应用程序的状态
使用步骤
- 在定义模块时,将namespaced属性设置为true
export default {
state,
getters,
...,
namespaced: true
}
- 在根模块中引入并注册该模块
//store/index.js
export default new vuex.Store({
modules: {
car,
goods
}
});
- 在组件中使用带有命名空间的模块
export default {
//在组件中使用带有命名空间的模块。在Vue组件中,我们可以通过使用模块的命名空间来访问该模块的状态、操作和getter
computed: {
...mapState('空间名', ['空间下的state'])
},
methods: {
...mapActions('空间名', ['空间下的action'])
}
}
严格模式
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。如果我们尝试直接修改状态,例如通过store.state.xxx = newValue的方式,会抛出一个错误。我们必须使用mutations来修改状态,例如通过store.commit(‘mutationName’, payload)的方式。这能保证所有的状态变更都能被调试工具跟踪到。
- 开启严格模式,仅需在创建 store 的时候传入 strict: true
- 不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。
const store = createStore({
strict: process.env.NODE_ENV !== 'production'
})
插件
在 Vuex 中,插件(plugins)是一个强大的功能,允许你在 Vuex store 初始化时注入一些功能或行为。插件可以用来实现日志记录、状态持久化、状态校验等功能。插件在 store 初始化时被执行,可以访问整个 store 的状态
== 后面会重点讲到:
基本用法
插件通常是一个函数,该函数接受一个 store 参数,并在 store 初始化时执行。插件可以监听 store 的状态变化,并根据需要执行一些操作
持久化
vuex + vuex-persistedstate
一个 Vuex 插件,它可以将 Vuex 的状态树持久化到浏览器的本地存储中,比如 localStorage 或 sessionStorage。这样,即使页面刷新或关闭浏览器,用户的状态也可以被保存下来,并在下次访问时恢复。
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
},
plugins: [
createPersistedState({
storage: window.localStorage, // 默认就是 localStorage
})
]
});
相关配置
storage
一个对象,默认情况下,它使用 localStorage 作为存储机制,但你也可以通过配置来指定使用 sessionStorage 或其他自定义的存储方法。
key
用于在 localStorage 或 sessionStorage 中存储状态的键名。默认是 ‘vuex’
paths
一个数组,指定哪些状态需要被持久化。如果未设置,所有状态都会被持久化。
reducer
一个函数,接收 state 作为参数并返回需要被持久化的状态。如果设置了 paths,则此选项会被忽略。
paths和reducer的区别
- 如果你只需要持久化特定的状态路径,使用paths会更简单直接:paths: [“user”, “role”]
- 如果你需要对状态进行复杂的处理或转换后再持久化,那么使用reducer会更加合适
- 它接收整个状态对象作为参数,并返回一个新的状态对象,这个新的状态对象就是需要被持久化的状态。reducer函数提供了更大的灵活性,你可以根据需要对状态进行过滤、转换或合并等操作。
reducer(state) {
return {
// 只持久化user和cart对象,并可以进一步处理这些对象
user: state.user,
cart: state.cart.items.map(item => ({ ...item, price: item.price * 0.9 })) // 假设我们对cart中的item价格打了9折
}
}