Vuex是做什么的?
- 官方解释:Vuex是一个专为Vue.js应用程序开发的状态管理模式。
- 它采用
集中式存储管理
应用的所有组件状态,并以相应的规则保证状态以一种可预测的方式发生变化。 - Vuex也集成到Vue的官方调试工具
devtools extension
,提供了诸如零配置的time-travel调试、状态快照导入导出等高级调试功能。
2. 状态管理到底是什么?
- 状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。
- 其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。
- 然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。
- 那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?
Vuex状态管理图例
简单的案例
实现一个简单的加减案例
-
首先,我们需要在某个地方存放我们的Vuex代码:
- 这里,我们先创建一个文件夹store,并且在其中创建一个index.js文件
- 在index.js文件中写入如下代码:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { count:0 }, mutations: { //方法 increment(state){ state.count++ } decrement(state){ state.count-- } }, })
-
其次,我们让所有的Vue组件都可以使用这个store对象
- 来到main.js文件,导入store对象,并且放在new Vue中
- 这样,在其他Vue组件中,我们就可以通过this.$store的方式,获取到这个store对象了
import Vue from 'vue' import App from './App.vue' import store from './store' new Vue({ store, render: h => h(App) }).$mount('#app')
-
使用Vuex的count
<template> <div> <p>{{count}}</p> <button @click="increment">+1</button> <button @click="decrement">-1</button> </div> </template> <script> export default { name: 'App', computed: { count: function () { return this.$store.state.count } }, methods: { increment: function() { this.$store.commit('increment') } decrement: function() { this.$store.commit('decrement') } } } </script>
以上,就是使用Vuex最简单的方式了
对以上使用步骤做一个简单的总结:
- 提取出一个公共的store对象,用于保存在多个组件中共享的状态
- 将store对象放置在new Vue对象中,这样可以保证在所有的组件中都可以使用到
- 在其他组件中使用store对象中保存的状态即可
通过this.$store.state.属性的方式来访问状态
通过this.$store.commit('mutation中的方法')来修改状态
-
注意事项:
- 我们通过提交mutation的方式,而非直接改变store.state.count。
- 这是因为Vuex可以更明确的追踪状态的变化,所以不要直接改变store.state.count的值。
- 同步操作可以直接通过mutations来修改,但异步不行,得先通过Actions
Getters基本使用
- 有时候,我们需要从store中获取一些state变异后的状态,比如下面的Store中:
获取学生年龄大于20的学生。const store = new Vue.store({ state: { students: [ {id:1, name: 'kk', age: 21}, {id:2, name: 'hh', age: 19}, ] } })
- 我们可以在Store中定义getters
getters: { more20Ages(state) { return state.students.filter(s => s.age >=20) } }
-
Getters作为参数和传递参数
- 如果我们已经有了一个获取所有年龄大于20岁学生列表的getters,那么代码可以这样写
getters: { more20Ages(state) { return state.students.filter(s => s.age >=20) }, more20AgesLength(state,getters) { return getters.more20Ages.length }, }
- getters默认是不能传递参数的,如果希望传递参数,那么只能让getters本身返回另一个函数。
//比如我们是希望根据年龄来获取用户信息 getters: { moreAgeStu(state) { return age => { return state.students.filter(s => s.age > age) } } }
Mutation状态更新
- Vuex的store状态的更新唯一方式:提交Mutation
- Mutation主要包括两部分
- 字符串的事件类型(type)
- 一个回调函数(handler),该回调函数的第一个参数就是state。
- mutations的定义方式:
mutations: {
increment(state) {
state.count++
}
}
- 通过mutation更新
increment: function () {
this.$store.commit('increment')
}
Mutation传递参数
- 在通过mutation更新数据的时候,有可能我们希望携带一些额外的参数
参数被称为是mutation的载荷(Payload) - Mutation中的代码
decrement(state,n) {
state.count -= n
}
decrement: function (count) {
//payload:负载
//普通的的提交封装
this.$store.commit('decrement',count)
}
- 但如果参数不是一个呢?
比如我们有很多参数需要传递
这个时候,我们通常会以对象的形式传递,也就是payload是一个对象
这个时候可以再从对象中取出相关的信息
changeCount(state,payload) {
state.count = payload.count
}
changeCount: function () {
this.$store.commit('changeCount',{count: 0})
}
Mutation提交风格
- 上面的通过commit进行提交是一种普通的方式
- Vue还提供了另外一种风格,它是一个包含type属性的对象
this.$store.commit({
type: 'changeCount',
count
})
- Mutation中的处理方式是将整个commit的对象作为payload使用,所以代码没有改变,依然如下:
changeCount(state,payload) {
state.count = payload.count
}
Mutation响应规则
- Vuex的store中的state是响应式的,当state中的数据发生改变时,Vue组件会自动更新
- 这就要求我们必须遵守一些Vuex对应的规则:
- 提前在store中初始化好所需的属性
- 当给state的对象添加新属性时,使用下面的方式:
方式一:使用Vue.set(obj,'newProp',123) Vue.set(state.info,'address','泉州')
方式二:用新对象给旧对象重新赋值
Mutation常量类型 - 概念
- 在用mutation时会有以下问题
- 在mutation中,我们定义了很多事件类型(也就是其中的方法名称)
- 当我们的项目增大时,Vuex管理的状态越来越多,需要更新状态的情况也越来也多,那么意味着Mutation中的方法越来越多
- 方法过多,使用者需要花费大量的经历去记住这些方法,甚至是多个文件来回切换,查看方法名称,甚至如果不是复制的时候,可能还会出现写错的情况。
- 改进措施
- 在store下新建一个mutation-types.js文件
export const INCREMENT = 'increment'
- 在提交方法地方导入mutation-types.js这个文件
import {INCREMENT} from './store/mutation-types.js'
提交方法就可以改成以下方式
addition() { this.$store.commit(INCREMENT) }
- store.jsmutation方法也需要先导入mutation-types.js
import {INCREMENT} from './store/mutation-types.js'
mutation方法就可以改成以下方式
[INCREMENT](state) { state.counter++ }
- 在store下新建一个mutation-types.js文件
Mutation同步函数
- 通常情况下,Vuex要求我们Mutation中方法必须是同步方法
- 主要原因是当我们使用devtools时,devtools可以帮助我们捕捉mutation的快照
- 但如果是异步操作,那么devtools将不能很好的追踪这个操作什么时候会被完成
Action的基本定义
- 当我们需要在Vuex中进行异步操作,比如网络请求,这时就可以用Action,它是用来代替Mutation进行异步操作
mutations: {
updateInfo(state) {
state.info.name = 'kk'
}
}
actions: {
//context上下文,这里就当做store对象
/**aUpdateInfo(context,payload) {
setTimeout( () => {
context.commit('updateInfo')
console.log(payload.message) //我是携带的信息
payload.success() //里面异步操作已经完成
},1000)
}**/
//可以优化返回一个promise
aUpdateInfo(context,payload) {
return new promise( (resolve,reject) => {
setTimeout( () => {
context.commit('updateInfo')
console.log(payload) //我是携带的信息
resolve('kkkkk')
},1000)
})
}
}
updateInfo() {
/**this.$store.dispatch('aUpdateInfo',{
message: '我是携带的信息',
success: () => {
console.log('里面异步操作已经完成')
}
})**/
//上述代码还可以优化成以下
this.$store.dispatch('aUpdateInfo','我是携带的信息').then(res => {
console.log('里面完成了提交')
console.log(res) //kkkkk
})
}
认识Module
- Module是模块的意思,为什么在Vuex中我们要使用模块呢?
- Vue使用单一状态数,那么也意味着很多状态都会交给Vuex来管理
- 当应用边得非常复杂时,store对象就有可能变得相当臃肿
- 为了解决这个问题,Vuex允许我们将store分割成模块(Module),而每个模块拥有自己的state、mutations、actions、getters等
const moduleA = {
state: { name: 'kk' },
mutations: {},
actions: {},
getters: {}
}
const moduleB = {
state: {},
mutations: {},
actions: {},
getters: {}
}
const store = new Vuex.Store({
modules: {
a:moduleA,
b:moduleB
}
})
store.state.a.name // ->kk
store.state.b // ->moduleB的状态