5分钟快速了解Vuex和Vuex的用法+状态持久化
介绍
Vuex 应用的核心就是 store(仓库),它包含了应用中的大部分状态。 Vuex的状态存储是响应式的,改变store中的的状态唯一途径就是通过mutation
1.state
管理Vuex中所有状态存储,类似Vue 实例中的 data
由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态
store/inde.js中
const store = createStore({
state: {
count:1
}
)}
在 Vue 组件中获得 Vuex 状态
方式一:(组件获取少的状态)
computed: {
count () {
return this.$store.state.count
}
}
方式二:利用mapState(组件需要获取多个状态)
import { mapState } from 'vuex'
export default {
data() {
return {
num: 10,
};
},
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// or传字符串参数 'count' 等同于 `state => state.count`
myCount: 'count',
// or为了能够使用 `this` 获取局部状态,必须使用常规函数
countFn (state) {
return this.num + state.count
}
})
}
当映射的计算属性的名称与 state 的子节点名称相同
import { mapState } from 'vuex'
export default {
computed: mapState({
// 映射 this.count 为 store.state.count
'count'
})
}
2.getter
相当于store 的计算属性(用来处理数据)
可接受传参(state,getters)
store/inde.js中
const store = createStore({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
// 把state传进出(一个参数)
doneTodos: (state) => {
return state.todos.filter(todo => todo.done) // { id: 1, text: '...', done: true }
},
// 把getters传进去(两个参数)
doneTodosCount: (state, getters) => {
return getters.doneTodos.length // 1
},
// getter 也可返回一个函数,来实现给 getter 传参(两个参数)
getTodoById: (state) => {
(id) => {return state.todos.find(todo => todo.id === id)}
}
}
})
在 Vue 组件中获得 Vuex 状态
方式一:(组件获取少的状态)
computed: {
doneTodos () {
return this.$store.getters.doneTodos // { id: 1, text: '...', done: true }
},
getTodoById () {
return this.$store.getters.getTodoById(2) // { id: 2, text: '...', done: false }
},
}
方式二:利用mapGetters(组件需要获取多个状态)
将 store 中的 getter 映射到局部计算属性
import { mapGetters } from 'vuex'
export default {
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
// 同名写法
...mapGetters([
'doneTodos',
'getTodoById'
])
// 不同名写法(把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`)
...mapGetters({
doneCount: 'doneTodosCount'
})
}
}
3.mutation(同步,通过commit触发)
更改 Vuex 的 store 中的状态的唯一方法(面试常问!!:因为如果通过mutation(同步函数)每一条都会被记录,devtools 都需能捕捉到前、后状态。如果是异步,就无法实现了,devtools就无法追踪到了,回调函数中进行的状态的改变都是不可追踪的)
可接受传参(state,(Payload))
store/inde.js中
const store = createStore({
state: {
num: 1,
num2:100,
num3:50
},
mutations: {
addNum(state,n) {
// 变更状态
state.num += n
},
subNum(state) {
// 变更状态
state.num2--
},
addOne(state)
// 变更状态
state.num3++
},
}
})
在 Vue 组件中获得 Vuex 状态
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'addOne', // 将 `this.addOne()` 映射为 `this.$store.commit('addOne')`
// `mapMutations` 也支持载荷:
'addNum' // 将 `this.addNum(num)` 映射为 `this.$store.commit('addNum', num)`
]),
...mapMutations({
reduce: 'subNum' // 将 `this.reduce()` 映射为 `this.$store.commit('subNum')`
})
}
}
==补充==
也可以使用常量当做变量名,多人协作时知道是干啥的函数(正常情况大可不用)
mutation-types.js
export const ADD_MUTATION = '对数字进行加法运算';
store→inde.js中
import * as types from './mutation-types';
const store = createStore({
state: {
num: 1
},
mutations: {
[types.ADD_MUTATION] (state,n) {
// 变更状态
state.num += n
}
}
})
4.action(异步,通过dispath触发)
也是通过提交的是 mutation进行改变,而不是直接变更状态
可以包含任意异步操作
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象参数(但它并不是store)
store/inde.js中
方法一:利用context参数
const store = createStore({
state: {
num: 0
},
mutations: {
addNum (state) {
state.num++
}
},
actions: {
increment (context) {
// 也可通过context.state 和 context.getters 来获取 state 和 getters
// 此处context.commit获取mutation中的addNum方法
context.commit('addNum')
}
}
})
方法二:参数解构来简化代码
import { userInfo } from '@/api/common';
const store = createStore({
state: {
num: 0,
userInfo:{age:18,name:'圆圆'}
},
mutations: {
addNum (state) {
state.num++
},
userInfos(state,info){
state.userInfos=info
}
},
actions: {
increment ({ commit,state,dispatch }) {
// commit:可调用mutation里面的方法修改state
// state:当前action中此方法使用到state中的数据
// dispatch:可调用actions中的其他异步方法
commit('addNum')
},
getUserInfo({ commit,dispatch }) {
await dispatch('increment') // 等待 increment 完成后,再执行下面代码(这是是为了说明可内部调用action中的其他方法)
return new Promise((resolve, reject) => {
userInfo().then((res) => {// 这里的userInfo为请求的接口、userInfo() 返回的是 Promise
commit('userInfos', res);
resolve(res);
});
});
},
}
})
在 Vue 组件中获得 Vuex 状态
方法一:
export default {
async created() {
await this.$store.dispatch('increment'),// 根据情况添加async和await
this.$store.dispatch('incrementBy', amount)
}
}
方法二:使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用
import { 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')`
})
}
}
5.Module
解决store过于庞大臃肿的情况,Vuex 允许我们将 store 分割成模块(module)
每个模块拥有自己的 state、mutation、action、getter
注意:最好不要在不同的模块中,写相同的变量和方法名
store/inde.js中
多个模块基本写法
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
模块内部
mutation中:接收参数(state,rootState)
getter中:接收参数(state, getters, rootState, rootGetters)
action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态,
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
},
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
// state.count是模块内state中的count
// rootState.count是根节点中state中的count(同名了,尽量不这样写,这里是为了强调模块内状态和根节点状态)
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
命名空间(可嵌套多层,随意)
以moduleA为例
modules/account.js中
export default {
namespaced: true,// 其成为带命名空间的模块(必须)
// 模块内容(module assets)
state: () => ({ ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
getters: {
//使用全局 state 和 getter,rootState 和 rootGetters 会作为第三和第四参数传入
isAdmin (state, getters, rootState, rootGetters) { ... } // -> getters['account/isAdmin']
},
actions: {
// 在这个模块中, dispatch 和 commit 也被局部化了
// 他们可以接受 `root` 属性以访问根 dispatch 或 commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // -> 'account/someGetter'(account模块中的getters中的方法)
rootGetters.someGetter // -> 'someGetter'(根节点getters方法)
dispatch('someOtherAction') // -> 'account/someOtherAction'(account模块中的actions中的方法)
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'(根节点actions中方法)
commit('someMutation') // -> 'account/someMutation'(account模块中的mutation中的方法)
commit('someMutation', null, { root: true }) // -> 'someMutation'(根节点mutation中的方法)
},
},
mutations: {
login () { ... } // -> commit('account/login')
},
}
==补充:若想在模块中注册全局action:可添加 root: true,并将这个 action 的定义放在函数 handler 中==
actions: {
someAction: {
root: true,
handler (namespacedContext, payload) { ... } // -> 'someAction'
}
}
store/index.js中
import Vue from 'vue';
import Vuex from 'vuex';
import account from './modules/account'
Vue.use(Vuex);
const store = {
state: {},
modules: {
account
}
};
export default new Vuex.Store(store);
在 Vue 组件中获得 Vuex 状态
方式一:(较繁琐)
computed: {
...mapState({
a: state => state.account.a,// 这里根据自己嵌套的层数书写
b: state => state.account.b
})
},
methods: {
...mapActions([
'state.account/someAction', // -> this['state.account/someAction']()
'state.account/otherAction' // -> this['state.account/otherAction']()
])
}
方式二:(稍微简化)
computed: {
...mapState('state.account', {
a: state => state.a,
b: state => state.b
})
},
methods: {
...mapActions('state.account', [
'someAction', // -> this.someAction()
'otherAction' // -> this.otherAction()
])
}
vuex数据持久化
vuex存储状态出现的问题:页面刷新后 vuex的数据会丢失,因为页面刷新会重新执行js脚本,数据是存在内存中的,页面刷新后这块空间自然就释放了没有了
数据持久化解决的问题:刷新不再丢失,插件变相把数据存在localStorage中
要求:
配合vuex的module模块化使用
安装插件vuex-persistedstate:npm i vuex-persistedstate或yarn add vuex-persistedstate
项目结构:
├─store
│ ├─modules
│ ├─├─currentPage.js
│ ├─plugin
│ ├─├─PersistedState.js
│ ├─index.js
currentPage.js
function defaultState() {
return {
myInfos: { name: '一瓶人间小样', age: 18 },//用户信息
}
}
export default {
namespaced: true,
state: defaultState(),
mutations: {
setMyInfos(state, payload) {
state.myInfos = payload;
}
}
}
PersistedState.js
import createPersistedState from 'vuex-persistedstate';// 导入插件
export default createPersistedState({
// 保存到 localStorage 的 key 值。已经确定的值,不要改就好了
key: 'my_persisted_state',
// 仅将特定的 state 值持久化到本地。例如:'a' => 'state.a' 对应的值持久化到本地。
paths: [
'currentPage',
]
});
index.js
import Vue from 'vue';
import Vuex from 'vuex';
import PersistedStatePlugin from '@/store/plugin/PersistedState';
import currentPage from './modules/currentPage'
Vue.use(Vuex);
const store = {
state: {
},
mutations: {
},
modules: {
currentPage
},
actions: {},
plugins: [PersistedStatePlugin]
};
export default new Vuex.Store(store);
如图:这里有坑,只有你在某个地方修改了持久化的状态,这里才会给您保存,如果你没有修改过,就不会有