Vuex 是什么

Vuex 是一个专为 Vue.js 应用程序开发的全局状态管理库,采用集中式存储管理应用的所有组件的状态,解决多组件数据通信

  1. Vue 官方搭配,专属使用 (类似于:vue-router),有专门的调试工具
  2. 集中式状态方案(操作更简洁)data() { return { 数据, 状态 }}
  3. 数据变化是可预测的(响应式)

image-20240822151237594

使用方法

在vuex 中this 指的是 store 实例, 可以通过 this.state 访问 state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
export default store
1
2
3
4
5
6
7
// main.js
import store from './store/index.js'

new Vue({
el: '#app',
store: store,
})

核心概念

state

  • 在组件中,通过 this.$store.state.属性名 来访问
  • 在模板中,可以省略 this,直接写成:{{$store.state.属性名}}
1
2
3
4
5
6
7
8
9
10
11
12
new Vuex.store({
// state 是定义响应式数据的地方
// state: {
// 属性名:属性值
// }
// 推荐
state: ()=>{
return {
属性名:属性值
}
}
})

getters

  • 在组件中通过 this.$store.getters.getter的名字 来访问
  • 在模板中,可以省略 this,直接写成:{{$store.getters.getter的名字}}
1
2
3
4
5
6
7
8
9
new Vuex.store({
// 省略其他...
getters: {
// state 就是上边定义的公共数据state
getter的名字1: function (state) {
return 要返回的值
},
},
})

mutations

mutations 中的每一项都是一个函数,每个函数有两个形参

  • 第一个参数表示当前的 state,Vuex 内部传入的
  • 第二个参数表示载荷,表示在调用 mutation 时传入的实参

调用方式

  • 在组件中使用 this.$store.commit(‘mutations的名字’, 参数)
  • 在 actions 中通过 context.commit(‘mutations的名字’, 参数)
1
2
3
4
5
6
7
8
9
10
11
12
new Vue.store({
// ...省略其他
mutations: {
// 每一项都是一个函数,可以声明两个形参
mutation名1: function(state, 载荷) {

},
mutation名2: function(state, 载荷) {

},
}
})

actions

  • 在组件中通过 this.$store.dispatch('actions的名字', 参数)
  • 在模板中,可以省略 this,直接写成:{{$store.dispatch.actions的名字}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
new Vuex.store({
// 省略其他...
actions: {
// context对象会自动传入,它与store实例具有相同的方法和属性
action的名字: function (context, 载荷) {
// 1. 发异步请求,请求数据
// 2. commit调用mutation来修改数据
// context.commit('mutation名', 载荷)
},
// action的名字: function ({context}, 载荷) {
// commit('mutation名', 载荷)
// },
},
})

modules

问题导入:

  • 随着项目越来越大,需要放在 vuex 中的数据越来越多,整个 store/index.js 中代码会越来越长,怎么办呢?

modules 的作用:

  • 拆分模板,把复杂的场景按模块来拆开

多模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
export default new Vuex.Store({
// state: 用来保存所有的公共数据
state: {},
getters: {},
mutations: {},
actions: {},
modules: {
模块名1:{
// namespaced 为 true,则在使用 mutations 时,就必须要加上模块名
namespaced: true,
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
},
模块名2:{
// namespaced不写,默认为 false,则在使用mutations时,不需要加模块名
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
}
}
})

也可以更进一步对文件进行拆分

|–store /
|——- index.js # 引入模块
|——- modules
|————– / mod1.js # 模块1
|————– / mod2.js # 模块2

1
2
3
4
5
6
 modules: {
// 模块名1
mod1.js,
// 模块名2
mod2.js
}

语法调整

  • 访问模块中的数据,要加上模块名
1
2
获取数据项: {{$store.state.模块名.数据项名}}
获取getters:{{$store.getters['模块名/getters名']}}

访问模块中的 mutations/actions

如果 namespaced 为 true,则需要额外去补充模块名
如果 namespaced 为 false,则不需要额外补充模块名

1
2
$store.commit('mutations名') // namespaced为false
$store.commit('模块名/mutations名') // namespaced为true

小结

使用了 modules 之后,在访问数据时就要额外添加 modules 的名字了

结论:在使用 modules 时,建议都给加上 namespaced!

结构优化

1
2
3
4
5
6
7
8
9
10
11
// store/modules/module1.js
const module1 = {
namespaced: true,
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
}

export default module1
1
2
3
4
5
6
7
8
9
10
// store/modules/module2.js
const module2 = {
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
}

export default module2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import module1 from './modules/module1'
import module2 from './modules/module2'

Vue.use(Vuex)

export default new Vuex.Store({
state: {},
getters: {},
mutations: {},
actions: {},
modules: {
module1,
module2
}
})

辅助函数

mapState

  • 直接使用:this.$store.state.state属性名

映射 辅助函数mapState

1
2
3
4
5
6
7
8
9
10
11
// 1. 导入辅助函数mapState,它是在vuex中定义的一个工具函数
// ES6 按需导入 import { mapState } from 'vuex'
import { mapState } from 'vuex'

computed: {
// 说明1:...对象 是把对象展开,合并到computed
// 说明2:mapState是一个函数
// ['数据项1', '数据项2']
...mapState(['state属性名']),
...mapState({'新名字': 'state属性名'}) // 改名
}
  • 使用辅助函数后:this.state属性名

mapGetters

  • 直接使用:this.$store.getters.getters名字

mapGetters 辅助函数

1
2
3
4
5
6
{
computed: {
...mapGetters(['getters名字']),
...mapGetters({'新名字': 'getters名字'}) // 改名
}
}
  • 使用辅助函数后:this.getters名字

mapMutations

  • 直接使用:this.$store.commit('mutation名', 参数)

mapMutations 辅助函数

1
2
3
4
5
6
{
methods: {
...mapMutations(['mutation名']),
...mapMutations({'新名字': 'mutation名'}) // 改名
}
}
  • 使用辅助函数后:this.mutation名

mapActions

  • 直接使用:this.$store.dispatch('action名', 参数)

mapActions 辅助函数

1
2
3
4
5
6
{
methods: {
...mapActions(['actions名']),
...mapActions({'新名字': 'actions名'}) // 改名
}
}
  • 使用辅助函数后:this.actions名

moudle访问指定模块

开启namespaced : namespaced: true

2.namespaced语法变化

原始语法

  • 全局的:指的是 store/index.js里面的 state、mutations、actions
  • 模块的:指的是模块内部的 state、mutations、actions
1
2
3
4
5
6
7
8
9
10
11
12
//state
全局的: $store.state.数据项名
模块的: $store.state.模块名.数据项名
//getters
全局的: $store.getters.getter
模块的: $store.getters['模块名/getters名']
//mutations
全局的: $store.commit('mutations名',载荷)
模块的: $store.commit('模块名/mutations名',载荷)
//actions
全局的: $store.dispatch('actions名',载荷)
模块的: $store.dispatch('模块名/actions名',载荷)

state

  • 直接使用:this.$store.state.模块名.xxx
  • map 辅助函数
1
2
3
4
5
{
computed: {
...mapState('模块名', ['xxx']),
}
}

getters

  • 直接使用:this.$store.getters.模块名.xxx
  • map 辅助函数
1
2
3
4
5
6
{
computed: {
...mapGetters('模块名', ['xxx']),
...mapGetters('模块名',{'新名字': 'xxx'})
}
}

mutations

  • 直接使用:this.$store.commit('模块名/mutation名', 参数)
  • map 辅助函数
1
2
3
4
5
6
{
methods: {
...mapMutations('模块名', ['xxx']),
...mapMutations('模块名',{'新名字': 'xxx'})
}
}

actions

  • 直接使用:this.$store.dispatch('模块名/action名', 参数)
  • map 辅助函数
1
2
3
4
5
6
{
methods: {
...mapActions('模块名', ['xxx']),
...mapActions('模块名',{'新名字': 'xxx'})
}
}

总结

辅助函数语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//state
全局的: ...mapState(['数据项名'])
模块的: ...mapState('模块名', ['数据项名'])
更改属性名: ...mapState('模块名', {'新名字': 'xxx'})
//getters
全局的: ...mapGetters(['数据项名'])
模块的: ...mapGetters('模块名', ['数据项名'])
更改属性名: ...mapGetters('模块名',{'新名字': 'xxx'})
//mutations
全局的: ...mapMutations(['mutaion名'])
模块的: ...mapMutations('模块名', ['mutaion名'])
更改属性名: ...mapMutations('模块名',{'新名字': 'xxx'})
//actions
全局的: ...mapActions(['action名'])
模块的: ...mapActions('模块名', ['action名'])
更改属性名: ...mapActions('模块名',{'新名字': 'xxx'})

vuex-persistedstate 持久化

下载插件

1
npm install --save vuex-persistedstate@3.2.1

在vuex中配置插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import Vue from 'vue'
import Vuex from 'vuex'
// 导入持久化插件
import creteState from 'vuex-persistedstate'

Vue.use(Vuex)

export default new Vuex.Store({
//配置持久化存储数据的 vuex 插件
plugins: [creteState()],
//1.state:存储数据
state: {
// 登录成功之后的 token 值
token: '',
},
//2.mutations:更新数据
mutations: {
//更新 token
updateToken(state, newToken) {
state.token = newToken
},
},
//3.actions: 异步更新数据
actions: {
},
modules: {
}
})

子模块访问根节点

state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const moduleA = {
namespaced: true,
state: () => ({
someData: 'ModuleA data'
}),
getters: {
someRootData(state, getters, rootState) {
return rootState.someRootData
}
}
}

const store = new Vuex.Store({
state: {
someRootData: 'Root data'
},
modules: {
a: moduleA
}
})

getters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const moduleA = {
namespaced: true,
getters: {
someRootGetter(state, getters, rootState, rootGetters) {
return rootGetters.someRootGetter
}
}
}

const store = new Vuex.Store({
getters: {
someRootGetter: (state) => {
return 'Root getter value'
}
},
modules: {
a: moduleA
}
})

带参数的 getters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const store = new Vuex.Store({
state: {
users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
]
},
getters: {
// 通过返回一个函数,使得 getter 可以接收参数
getUserById: (state) => (id) => {
return state.users.find(user => user.id === id)
}
}
})

// 使用 在组件中
computed: {
user() {
// 传递用户 ID 作为参数
return this.$store.getters.getUserById(2) // 返回 { id: 2, name: 'Bob' }
}
}

// 或者在任何需要的地方
const user = store.getters.getUserById(2)
console.log(user) // 输出: { id: 2, name: 'Bob' }

actions

调用根模块的 action 并传递参数 dispatch(‘根模块actions名’, payload, { root: true })

在子模块的 actions 中访问根模块的 state 和 getters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const moduleA = {
namespaced: true,
actions: {
someAction({ state, commit, rootState, rootGetters }) {
console.log(rootState.someRootData) // 访问根模块的 state
console.log(rootGetters.someRootGetter) // 访问根模块的 getters

// 调用根模块的 action
dispatch('someRootAction', null, { root: true })
}
}
}


const store = new Vuex.Store({
state: {
someRootData: 'Root data'
},
getters: {
someRootGetter: (state) => 'Root getter value'
},
modules: {
a: moduleA
}
})

在子模块的 actions 中调用根模块的 mutations 和 actions

调用根模块的 mutations 并传递参数 commit(‘根模块mutations名’, 参数, { root: true })

mutations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const moduleA = {
namespaced: true,
actions: {
someAction({ commit, dispatch }) {
// 调用根模块的 mutation null 向someRootMutation 传递的参数
commit('someRootMutation', null, { root: true })
}
}
}

const store = new Vuex.Store({
mutations: {
someRootMutation(state) {
console.log('Root mutation called')
}
},
actions: {
someRootAction({ commit }) {
console.log('Root action called')
}
},
modules: {
a: moduleA
}
})

总结

vuex中有哪些配置项(核心概念)作用是什么

  • state作用: 负责存储数据
  • getters作用:state计算属性(有缓存)
  • mutaions作用:负责同步更新state数据
    • mutaions是唯一可以修改state数据的方式
  • actions作用:负责异步操作,例如发送网络请求,将请求到的数据通过commit触发mutaions来修改state
  • modules作用:负责模块化管理vuex数据

如果直接在页面中或者actions中修改state里面的数据报错吗?
不会,但是直接修改 state 的代码无法被 Vuex 的调试工具(如 Vue DevTools)正确追踪和记录,难以调试应用中的问题
如果你在异步操作中直接修改了 state,可能导致状态不一致,因为异步操作可能在不同的时间段触发多次状态修改

什么情况下只能通过mutations修改state

开启vuex 严格模式,如果使用其他方式更改state会报错

严格模式主要用于开发环境,它可以帮助你确保所有状态修改都遵循 Vuex 的最佳实践。通过捕捉不合法的状态修改,可以在开发早期发现潜在的错误,避免难以调试的状态管理问题

1
2
3
4
5
6
7
8
9
10
11
12
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
},
strict: true // 开启严格模式
// strict: process.env.NODE_ENV !== 'production' // 生产环境关闭严格模式
})