vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。最大的特点就是响应式。
什么状态时需要在多个组件间共享?
- 大型项目中,需要在多个界面间的共享的状态
- 如用户的登录状态、用户名称、头像、地理位置信息等
- 如商品的收藏、购物车中的物品等
一般父传子可以用props,但如何两个组件之间没有直接相关联的关系,就需要引入vuex
// 1 安装
npm install vuex --save
// 2.1 引入(一般main.js里引入,需要创建一个/scr/store文件夹,并在内新建一个index.js文件后引入)
import Vuex from 'vuex'
// 2.2 安装
Vue.use(Vuex);
// 2.3 创建对象
const store = new Vuex.Store({ })
// 2.4 导出storer对象
export default store
// 3 回到main.js中引入
import store from './store'
简单使用
// 在vuex对象里,新建一个state属性,用于存放公共数据
const store = new Vuex.Store({
state: {
xmsg: 'a share msg.'
}
})
// 在组件模板中
<h1>{{$store.state.xmsg}}</h1>
// 绑定事件时,可以通过此种方式操作,但官方不推荐
<button @click='$store.state.xmsg'>按钮</button>
因为如果绑定到某个单一页面中时,就可能很难追踪到到底是哪个页面发生了操作,因此官方推荐以下方法,通过 Actions-->Mutations->State 的流程,并在Mutations中帮你记录发生操作的页面对象(可以在Vue Devtools工具中查看),更方便调试。
其实可以直接跳过Actions,通过 Mutations->State 的流程来实现,但这只仅限于同步操作,如果存在异步操作,就不能跳过。
故Actions主要用于异步操作,即发送网络请求时,接收后端数据(Backend API)。
state
存放公共状态/共享数据
vue提出一个单一状态树(Single Source of Truth)的概念,即推荐你的状态信息只保存到单一的一个Store对象中,如果创建多个Store对象,那么之后的管理和维护等都会变得特别困难。所以Vuex使用了单一状态树来管理应用层级的全部状态,它能够以最直接的方式找到某个状态的片段,并在之后的维护和调试过程中,方便管理和维护。
改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。
getters
类似于计算属性,存放公共的计算属性,也可用作过滤和传参
如果我们已经有了一个获取所有年龄大于20岁学生列表的getters,那么代码可以这样来写
getters:{ greaterAgesstus: state => { return state.students.filter(s => s.age >= 20) }, greaterAgescount: (state, getters) => { return getters.greaterAgesstus.length } }getters 默认不能传递参数,如果希望传递参数,那么只能让getters本身返回另一个函数。
比如上面的案例中,我们希望根据ID获取用户的信息
getters:{ stuByID: state => { return id => { return state.students.find(s => s.id === id) } } }
mutations
状态更新,用于存放公共操作的函数
- ==Vuex 的 store 状态更新的唯一方式是提交 Mutation==
- Mutation 主要包含两部分:字符串的事件类型(type)和一个回调函数(handler),该回调函数的第一个参数自动默认传入一个state参数,第二个参数的载荷(payload),在更新数据时顺带传入额外的参数,payload也可以是一个对象
// 计数器案例
<h1>{{$store.state.count}}</h1>
<button @click='add'>+</button>
<button @click='sub'>-</button>
<button @click='addNum(10)'>+10</button>
methods:{
add(){ this.$store.commit('increment') },
sub(){ this.$store.commit('decrement') },
addNum(num){ this.$store.commit('incrementNum', num) },
// 特殊的提交封装 (使用payload传入多个对象参数时)
addNum(num){
this.$store.commit({
type: 'incrementNum2',
num
}),
}
}
// 通过vuex的commit方法来间接操作
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
},
decrement(state) {
state.count--
},
incrementNum(state, num) {
state.count += count
},
incrementNum2(state, payload) {
state.count += payload.count
}
}
})
Mutations d响应原则:1.必须提前在store中初始化所需的属性;2.当需要做响应式操作时,通过 set() 和 delete() 方法
mutations: {
updateInfo(state) {
// 不能用这种方法 无法实现响应式
state.info['address'] = 'beijing';
delete state.info.msg;
// 只能通过以下方法
Vue.set(state.info, 'address', 'beijing');
Vue.delete(state.info, 'msg');
}
}
通常情况下,Vuex要求 Mutation中定义的方法必须是同步的,主要的原因是当我们使用devtools时,可以devtools可以帮助我们捕捉mutation的快照;但如果是异步操作,那么 devtools 将不能很好的追踪这个操作什么时候会被完成。
要实现异步操作,必须使用
actions在vuex中的mutations方法,想调用mutations中的另外一方法,还是用commit,只需要使用this.commit('function'),当前的this指向的就是当前模块中的mutations;
传多个参数的写法:
此时参数不能继续在后面加,后面的参数无效,传进去的参数为undefined;
官网的解释:In most cases, the payload should be an object so that it can contain multiple fields, and the recorded mutation will also be more descriptive;
所以,我们可以将参数以对象的方式传进去,多个属性就是多个参数了。
actions
默认传入一个参数是 context
mutations: {
updateInfo(state) {
state.info.msg = 'hello';
}
actions: {
aUpdateInfo(context) {
// 模拟异步操作
setTimeout(() => {
// 不能这样操作 state只能在mutations里实现
// context.state.info.msg = 'hello'
context.commit(updateInfo)
}, 1000)
}
}
metheds: {
updateInfo() {
this.$store.dispatch('aUpdateInfo')
}
}
modules
Module是模块的意思,为什么在Vuex中我们要使用模块呢
Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理。 当应用变得非常复杂时,store对象就有可能变得相当腌肿 为了解决这个问题,Vuex允许我们将store分割成模块(Module),而每个模块拥有自己的state、mutations、actions、getters等
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
store 目录结构
/store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
├── getters.js # 根级别的 getters
└── /modules
├── modulesA.js # 模块A
└── modulesB.js # 模块B
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations
import actions from './actions
import getters from './getters
import moduleA from './modules/modulesA
import moduleB from './modules/modulesB
Vue.use(Vuex);
const state = {
...// state 无需单独导入
}
const store = new Vuex.Store({
state,
mutations,
actions,
getters,
modules: {
a: moduleA,
b: moduleB
}
})
export default store
// mutations.js actions.js getters.js 单独以文件形式存放
export default {
...
}