前言
在进行复杂一点的项目开发时,我们会发现只是普通的组件间的数据传递已经不足以满足我们的需求,所以我们需要引入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官网,如果有什么错误欢迎指出哦~