共计 5206 个字符,预计需要花费 14 分钟才能阅读完成。
前言
在进行复杂一点的项目开发时,我们会发现只是普通的组件间的数据传递已经不足以满足我们的需求,所以我们需要引入 Vue 中管理状态工具 –Vuex
正文
与全局变量的差别
- 状态存储是响应式的,如果组件中使用了 store 中的值,那么 store 改变时也会影响到组件状态的改变;
- 改变 store 的唯一方法是提交 (commit)mutations 中的值,也就是使用 commit 命令去触发 mutations 中的方法。
// 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)
const store = new Vuex.Store({
state: {count: 0},
mutations: {increment (state) {state.count++}
}
})
store.commit('increment')// 通过 commit 去触发 mutations 中的 increment 方法
console.log(store.state.count) // -> 1
State
state: 单一状态树
在 Vuex 中使用 state 保存了全部的应用层级的状态,我们可以通过 store.state.[变量名] 去获取状态值,在通常的情况下,我们可以直接在每个需要使用 state 的组件中引入 store, 但是这种方式会频繁地在各个组件中导入,会影响性能;所以一般的解决方法是先在根实例中注册 store,该实例会注册到子组件中,这样的话,在子组件中我们就可以通过 this.$store.state.[变量名] 去获取到想要的状态。
// 第一种方式:在使用到 state 的组件中引入 store
const Counter = {template: `<div>{{ count}}</div>`,
computed: {count () { // 通过计算属性去返回状态
return store.state.count
}
}
}
// 第二种方式:在根组件的实例中,把 store 对象提供给“store”选项
const app = new Vue({
el: '#app',
store,
components: {Counter},
template: `
<div class="app">
<counter></counter>
</div>
`
})
const Counter = {template: `<div>{{ count}}</div>`,
computed: {count () {return this.$store.state.count // 在子组件中只需要使用 this.$store 就可以使用}
}
}
mapState: 可以同时获得 state 中的多个状态
前面说 state 的使用方法时,我们说到可以通过计算属性的去拿到想要获取的状态,但是如果我们要在同一个组件中获取多个状态时,我们就可以利用 mapState 了,下面是它的用法:
// 在 store 实例的 state 中存放值
state: {
name: '',
age: '',
sex: ''
}
// 在子组件的计算属性中去获取 store 中存放的值
compute: {...mapState(['name'])
}
Getter
getter: store 的计算属性
getter 主要是用来存放由 state 派生出的一些状态,如果很多组件都需要使用到这个状态,就需要在每个组件中根据 state 计算再计算一遍,这样就容易造成代码的冗余。store 中提供了一个 getter 属性,可以专门用来存放 store 中的 state 衍生出来的状态,如下面的用法:
// state 中存放的是 todos 属性
const store = new Vuex.Store({
state: {
todos: [{ id: 1, done: true},
{id: 2, done: false}
]
},
getters: {// getters 中存放的是由 state 派生出来,会多次用到的值
doneTodos: state => {// 第一个参数是 state
return state.todos.filter(todo => todo.done)
}
}
})
两种访问形式:通过属性访问和通过方法访问
- 通过属性访问
可以通过 store.getters 去获取 Getter 对象,然后通过访问属性的方式去获取 getters 中的内容,比如下例:
store.getters.doneTodos // -> [{id: 1, done: true}]
getters 也可以将自己作为参数使用,在方法中直接获取 getters 中的内容:
getters: {
// 获取 doneTodos 的长度
doneTodosCount: (state, getters) => {return getters.doneTodos.length}
}
- 通过方法访问
可以通过在 getter 中返回一个函数来给 getter 传参,这个比较适用于查找 store 中的数组,比如下面的用法:
getters: {
// 通过给 getTodoById 传入 id 来获取对应的项
getTodoById: (state) => (id) => {return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> {id: 2, done: false}
- 两种访问方式的区别
使用属性的方式去访问有缓存,通过方法去访问的话,每次都会被调用一次,不会有缓存。
mapGetters: 辅助函数
mapGetters 和 mapStates 是一样的作用,都是为了便于在子组件中获取多个状态,用法也类似,这里就不多说明了。
Mutations
在 Vuex 中,mutations 的角色就相当于是 vue 中的 methods,它可以用来存放方法,mutations 中的类型名就相当于是 methods 中的方法名,每个 mutation 中的会有一个回调函数,但是在 methods 中可以没有返回值。
const store = new Vuex.Store({
state: {count: 1},
mutations: {increment (state) {
// 变更状态
state.count++
}
}
})
// 通过 commit 去使用 mutations 中的方法,这也是改变 state 中的状态的唯一方法
store.commit('increment')
在 commit 的时候也可以传入额外的参数,即 mutation 的载荷(payload),通常这个参数会传入一个对象,如下面的用法:
mutations: {increment (state, payload) {state.count += payload.amount}
}
// 在 commit 的时候给他传递一个对象,这样的好处是更加易懂明了
store.commit('increment', {amount: 10})
// 还可以使用对象的方式去提交
store.commit({
type: 'increment',
amount: 10
})
使用常量去代替 Mutations 事件类型
在一个大型项目中,Mutations 中的事件可能会比较多,这时将每种类型都用一个常量去表示,然后将这些常量放在一个静态文件中,打开静态文件,就可以一目了然地看到 Mutations 中有哪些事件,比如下面的用法:
// mutation-types.js
// 在存放类型名的文件中将该类型常量抛出来
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
// 在 store 中引入该常量
import Vuex from 'vuex'
import {SOME_MUTATION} from './mutation-types'
const store = new Vuex.Store({state: { ...},
mutations: {[SOME_MUTATION] (state) {// ...}
}
})
!mutation 必须为同步函数
之前我们说到过如果想要改变 state,唯一的方法就是使用 commit 去改变,这样做是因为我们想要更明确地跟踪 state 状态的变化。如果 mutation 是异步函数的话,就不知道 state 具体是什么时间发送变化的,这就和之前设计的初衷相悖了。还有可能有多个异步 mutation 时,使用 commit 去改变的话就分辨不出来是哪个先进行回调,所以这也是比较麻烦的事。
Action
action 和 mutation 的关系
为了解决异步回调的问题,Vuex 中定义了一个类似于 mutation 的 action,action 是专门处理异步问题的,它接受一个和 store 变量具有相同属性和方法的对象 –context,所以可以用该对象去调用 commit 进行提交。
action 和 mutation 有以下两点区别:
- action 提交的是 mutation 而不是直接改变 state;
- action 中可以包含任何异步操作。
const store = new Vuex.Store({
state: {count: 0},
mutations: {increment (state) {state.count++}
},
// 在 actions 中去调用 mutations 中的方法
actions: {increment (context) {context.commit('increment')
}
}
})
之前说过在 action 中可以提交 mutations,但是它不仅仅是用来提交同步函数的,还可以在其中执行异步操作,如下用法:
// 在 actions 中执行异步操作
actions: {incrementAsync ({ commit}) {setTimeout(() => {commit('increment')
}, 1000)
}
}
使用 dispatch 分发 action
// 可以通过 store.dispatch 去触发 action
store.dispatch('incrementAsync')
// 和 commit 类似,dispatch 也可以使用对象的方式载荷发布
store.dispatch('incrementAsync', {amount: 10})
store.dispatch({
type: 'incrementAsync',
amount: 10
})
在组件中分发 action
在组件中分发 action 的方式有两种:
// 使用 this.$store.dispatch 分发
this.$store.dispatch('incrementAsync')
// 使用 mapActions 将组件中的方法映射为 store.dispatch 调用
import {mapActions} from 'vuex'
export default {
methods: {
...mapActions([// 将 this.increment() 映射成 this.$store.dispatch('increment')
'increment',
// 将 this.increment(amount) 映射成 this.$store.dispatch('increment', amount)
'incrementBy'
]),
...mapActions([// 将 this.add() 映射成 this.$store.dispatch('increment')
add: 'increment'
])
}
}
结合 async 和 await 去分发 action
使用 dispatch 返回的是 promise 对象,并且在 dispatch 中也可以处理 promise 对象,用法如下:
actions: {
// actionA 返回的是一个 promise 对象
actionA ({commit}) {return new Promise((resolve, reject) => {setTimeout(() => {commit('someMutation')
resolve()}, 1000)
})
}
}
直接处理
store.dispatch('actionA')
在 action 中处理
actions: {actionB ({ commit, dispatch}) {return dispatch('actionA').then(() => {commit('someMutation')
})
}
}
综合运用
// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {async actionA ({ commit}) {commit('gotData', await getData())
},
async actionB ({dispatch, commit}) {await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
总结
基本内容就是这么多了,项目中目前使用到的就是这些,这篇文章只是对 Vuex 常用内容的一个总结,更多的东西可以去看 Vuex 官网,如果有什么错误欢迎指出哦~