目录
  1. 1. Vuex 状态管理
  2. 2. 基本步骤
    1. 2.1. 安装
    2. 2.2. 导入
    3. 2.3. 创建 store
    4. 2.4. 挂载 Vue 实例
  3. 3. 使用
    1. 3.1. State
    2. 3.2. Getters
      1. 3.2.1. Getter 的第二参数
      2. 3.2.2. Getter 利用函数传参
    3. 3.3. Mutations(同步)
      1. 3.3.1. mutations 传参(Payload)
      2. 3.3.2. 对象方式提交 mutation
    4. 3.4. Actions(异步)
      1. 3.4.1. Actions 传参及对象方式分发
    5. 3.5. 模块化 Module
      1. 3.5.1. 局部状态
    6. 3.6. 添加和删除状态
    7. 3.7. 组件辅助函数
      1. 3.7.1. mapState
      2. 3.7.2. mapGetters
      3. 3.7.3. mapMutations
      4. 3.7.4. mapActions
前端状态管理 Vuex 使用总结 - 前端随笔

Vuex 状态管理

Vuex 状态管理

基本步骤

  1. 安装 Vuex
  2. 导入 Vuex ,使用插件 Vue.use(Vuex)
  3. 创建 store
  4. 挂载 Vue 实例;

安装

生产依赖

1
$ npm install --save vuex

导入

1
2
3
4
5
// ./src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

创建 store

与别的插件不同,vuex 创建 store 实例是通过 new Vuex.Store()

1
2
3
4
5
6
7
8
9
10
// ./src/store/index.js
const store = new Vuex.Store({
state: {},
mutations: {},
actions: {},
getters: {},
modules: {}
})

export default store // 导出 store

挂载 Vue 实例

1
2
3
4
5
6
7
8
// ./src/main.js
import Vue from 'vue'
import store from './store'

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

使用

State

组件内部可通过 this.$store.state.xxx 获取相应状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>
<h1>{{ $store.state.title }}</h1>
<p>{{ content }}</p>
</div>
</template>

<script>
export default {
data() {
return {
content: this.$store.state.content
}
}
}
</script>

Getters

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

  1. store 对象中定义 getters 方法; 每个 Getter 接受 state 作为其第一个参数:
1
2
3
4
5
6
7
8
9
10
11
12
13
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: (state) => {
return state.todos.filter((todo) => todo.done)
}
}
})
  1. 组件内部通过 this.$store.getters 访问 :
1
2
3
this.$store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
// 此处不能加能写成 this.$store.getters.doneTodos() 方法。
// 当 getter 只是返回结果时,访问只能以属性方式访问。

Getter 的第二参数

1
2
3
4
5
6
7
8
9
getters: {
doneTodos: (state) => {
return state.todos.filter((todo) => todo.done)
}
doneTodosCount: (state, getters) => {
// 第二参数为 getters 对象
return getters.doneTodos.length
}
}
1
store.getters.doneTodosCount // -> 1

Getter 利用函数传参

可以通过让 getter 返回一个函数,来实现给 getter 传参。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
getters: {
// ...
getTodoById(state) {
return function(id) { // 传入参数
return state.todos.find(todo => todo.id === id)
}
}
}

// or 箭头函数
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
1
this.$store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

Mutations(同步)

  1. store 对象中定义 mutations 方法(mutation 必须是同步操作);

    每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
// ./src/store/index.js
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) { //
state.count++
},
decrement(state) {
state.count--
}
})
  1. 组件内部通过 this.$store.commit() 提交 mutations
1
this.$store.commit('increment')

mutations 传参(Payload)

1
2
3
4
5
6
// ...
mutations: {
increment (state, n) {
state.count += n
}
}
1
this.$store.commit('increment', 10)

大多数情况下,载荷 payload 应该是一个对象:

1
2
3
4
5
6
// ...
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
1
2
3
this.$store.commit('increment', {
amount: 10
})

对象方式提交 mutation

直接提交包含 type 属性的对象,type 指明方法名,且整个对象均作为payload 传递给 mutation 方法,即整个对象将作为 mutation 的第二参数。

1
2
3
4
5
this.$store.commit({
type: 'increment',
amount: 10,
...
})
1
2
3
4
5
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}

Actions(异步)

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。

  • Action 可以包含任意异步操作。

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象参数,因此可以调用 context.commit 提交一个 mutation,或者通过 context.statecontext.getters 来获取 state 和 getters。

  1. store 对象中定义 actions 方法(通过提交 mutation 来更新状态);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
increment(context) {
// context 上下文,相当于 store 本身。
setTimeout(() => {
// action 可包含异步操作
context.commit('increment') // context.commit() 提交 mutation
}, 1000)
}
}
})
  1. 组件内部通过 this.$store.dispatch() 触发 action
1
this.$store.dispatch('increment')

Actions 传参及对象方式分发

Actions 支持同 Mutations 一样可通过载荷方式传参和对象方式进行分发:

1
2
3
4
5
6
7
8
9
10
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})

// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})

模块化 Module

Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}

const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}

const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})

局部状态

对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。对于模块内部的 action,局部状态通过 context.state 暴露出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const moduleA = {
state: { count: 0 }, // 局部状态
mutations: {
increment(state) {
// 局部状态
state.count++
}
},
getters: {
doubleCount(state) {
// 局部状态
return state.count * 2
}
},
actions: {
incrementIfOddOnRootSum(context) {
if (context.state % 2 === 1) {
// context.state 局部状态
context.commit('increment')
}
}
}
}

添加和删除状态

Vuex 中,只有在 state 定义的状态才会被实时监听,直接在 mutations 中为某一状态添加对象或属性,是不会被响应式处理的。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
state: {
info: {
name: 'Vincent',
age: 18
}
},
mutations: {
updateInfo(state) {
state.info['location'] = 'Guangzhou' // 添加属性
delete state.info.age // 删除属性
}
}
// 以上这样直接添加或删除属性,不会被实时监听,如果组件有访问使用该状态,即使修改了状态,组件也不会实时更新。

正确地添加和删除状态是使用 Vue.set()Vue.delete()

1
2
3
4
5
6
mutations: {
updateInfo(state) {
Vue.set(state.info, 'location', 'Guangzhou') // 添加属性
Vue.delete(state.info, 'age') // 删除属性
}
}

组件辅助函数

mapState

在组件中可以使用 mapState 辅助函数将状态映射成计算属性,减少计算属性声明带来的冗余。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex' // 引入辅助函数

export default {
// ...
computed: {
localComputed() { /* ... */ }, // 组件内部 computed
...mapState({ // 对象解构来快速将 state 映射成 computed

count: state => state.count, // this.count => this.$store.state.count

countAlias: 'count', // 等同于 `state => state.count`

countPlusLocalState (state) { // 为了能够使用 `this` 获取局部状态,必须使用常规函数
return state.count + this.localCount
}
},
// 当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
...mapState([
// 映射 this.count => this.$store.state.count
'count'
])
})
}

mapGetters

同上,映射 getters => computed :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { mapGetters } from 'vuex'

export default {
// ...
computed: {
// 同名,传入字符串数组,
...mapGetters([
// this.doneTodosCount => this.$store.getters.doneTodosCount
'doneTodosCount',
'anotherGetter'
]),
// or 重命名,传入对象
...mapGetters({
doneCount: 'doneTodosCount'
// this.doneCount => this.$store.getters.doneTodosCount
})
}
}

mapMutations

同上,映射 mutations => methods :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { mapMutations } from 'vuex'

export default {
// ...
methods: {
...mapMutations([
'increment', // this.increment() => this.$store.commit('increment')

// mapMutations 也支持载荷:
'incrementBy' // this.incrementBy(amount) => this.$store.commit('incrementBy', amount)
]),
...mapMutations({
add: 'increment' // this.add() => this.$store.commit('increment')
})
}
}

mapActions

同上,映射 actions => methods :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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')
})
}
}

参考文献:

文章作者: Vincent F0ng
文章链接: https://vincef0ng.cn/post/vuex/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Vincent F0ng
请喝奶茶
  • 微信
  • 支付宝
领红包

评论(支持Markdown)