目录
2、getters:由基本数据派生/计算得出的数据(相当于State的计算属性)
4、actions: 异步通过提交mutation更改state
在不同组件,层级比较复杂,需要不同组件的某些状态保持同步,但是又不是单纯的父子关系。需要响应各个地方的更新变化,并保持一致,就需要用到Vuex,典型的像各个组件同步购物车数据/消息列表
1. vue中各个组件之间传值
1.父子组件
父组件-->子组件,通过子组件的自定义属性:props
子组件-->父组件,通过自定义事件:this.$emit('事件名',参数1,参数2,...);
2.非父子组件或父子组件
通过数据总数Bus,this.$root.$emit('事件名',参数1,参数2,...)
3.非父子组件或父子组件
更好的方式是在vue中使用vuex
方法1: 用组件之间通讯。这样写很麻烦,并且写着写着,估计自己都不知道这是啥了,很容易写晕。
方法2: 我们定义全局变量。模块a的数据赋值给全局变量x。然后模块b获取x。这样我们就很容易获取到数据
Vuex 是什么?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension
,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
快速理解:state存状态,只有mutation可以操作在state存取状态(同步方式),actions是异步方法(通常是在这里向后台发请求拿数据)来操作mutation存取数据,不同组件内要运用state的状态,只需this.$store.state.xxx,调用actions只需this.$store.dispatch('xxx',xx)
五个核心的理解:
1、state: 存放需要管理的状态
用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT )”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。
所有的状态存储在一个公用的state中,组件使用 this.$store.state即可调用
mapState
辅助函数(语法糖)
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
可以简化成下面的写法
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
或者count:state=>state.count //理解为传入state对象,修改state.count属性
])
computed: {
localComputed () { /* ... */ },
// 使用对象展开运算符将此对象混入到外部对象中
...mapState({
// ...
})
}
在computed 使用,既可以随时监听变化,写法变得简单,在组件内就可以简单地通过this.count来引用
...mapState让你可以既有需要mapState的对象又有别的外部的对象
2、getters:由基本数据派生/计算得出的数据(相当于State的计算属性)
从state基本数据派生的数据 对state的数据进行加工以后再让组件调用
getters接收state作为其第一个参数,接受其他 getters 作为第二个参数,如不需要,第二个参数可以省略如下例子:
const store = new Vuex.Store({
state: {
count:0
},
getters: {
// 单个参数
countDouble: function(state){
return state.count * 2
},
// 两个参数
countDoubleAndDouble: function(state, getters) {
return getters.countDouble * 2
}
}
})
与state一样,我们也可以通过Vue的Computed获得Vuex的getters。
const app = new Vue({
//..
store,
computed: {
count: function(){
return this.$store.state.count
},
countDouble: function(){
return this.$store.getters.countDouble
},
countDoubleAndDouble: function(){
return this.$store.getters.countDoubleAndDouble
}
},
//..
}
基础用法:
main.js:
const store = new Vuex.Store({
state: {
list: [1, 3, 5, 7, 9, 20, 30]
},
getters: {
filteredList: state => {
return state.list.filter(item => item > 5)
}
}
})
index.vue:
<script>
export default {
name: "index.vue",
computed: {
list() {
return this.$store.getters.filteredList;
}
}
}
</script>
内部依赖
getter 可以依赖其它已经定义好的 getter。比如我们需要统计过滤后的列表数量,就可以依赖之前定义好的过滤函数。
main.js:
const store = new Vuex.Store({
state: {
list: [1, 3, 5, 7, 9, 20, 30]
},
getters: {
filteredList: state => {
return state.list.filter(item => item > 5)
},
listCount: (state, getters) => {
return getters.filteredList.length;
}
}
})
index.vue:
<template>
<div>
过滤后的列表:{{list}}
<br>
列表长度:{{listCount}}
</div>
</template>
<script>
export default {
name: "index.vue",
computed: {
list() {
return this.$store.getters.filteredList;
},
listCount() {
return this.$store.getters.listCount;
}
}
}
</script>
3、Mutations:修改state的唯一方法(同步!)
提交mutation是更改Vuex中的store中的状态的唯一方法。
mutation必须是同步的,如果要异步需要使用action。
使用vuex修改state时,有两种方式:
1)可以直接使用 this.$store.state.变量 = xxx;
2)this.$store.dispatch(actionType, payload) (使用actions)或者 this.$store.commit(commitType, payload)
mutaitions 处理同步事件
每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,提交载荷作为第二个参数。(提交荷载在大多数情况下应该是一个对象),提交荷载也可以省略的。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
//无提交荷载
increment(state) {
state.count++
}
//提交荷载
incrementN(state, obj) {
state.count += obj.n
}
}
})
你不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你需要以相应的 type 调用 store.commit 方法:
//无提交荷载
store.commit('increment')
//提交荷载
store.commit('incrementN', {
n: 100
})
对象风格的提交方式
我们也可以使用这样包含 type 属性的对象的提交方式。
store.commit({
type: 'incrementN',
n: 10
})
Mutations 需遵守 Vue 的响应规则
- 最好提前在你的 store 中初始化好所有所需属性。
- 当需要在对象上添加新属性时,你应该
- 使用
Vue.set(obj, 'newProp', 123)
, 或者 - 以新对象替换老对象。例如,利用对象展开运算符我们可以这样写
state.obj = {...state.obj, newProp: 123 }
- 使用
- 当需要在对象上添加新属性时,你应该
与其他辅助函数类似,你可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。
import { mapMutations } from 'vuex'
export default {
//..
methods: {
...mapMutations([
'increment' // 映射 this.increment() 为 this.$store.commit('increment')
]),
...mapMutations({
add: 'increment' // 映射 this.add() 为 this.$store.commit('increment')
})
}
}
4、actions: 异步通过提交mutation更改state
处理异步请求,在异步请求的回调函数当中去触发mutaitions中的函数,来同步的改变mutaitions中的值,像一个装饰器,包裹mutations,使之可以异步
Action 类似于 mutation,不同在于:
- Action 通过提交 mutation 来间接修改状态, 而不是直接变更状态。
- Action 可以包含任意异步操作。
- 发请求一般就写再actions里
我们用如下例子来结束actions:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
setInterval(function(){
context.commit('increment')//提交mutation来修改state
}, 1000)
}
}
})
注意:Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象(一般可以写成{commit, state}就足够了),因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
分发actions
在具体的组件中,Action 通过 store.dispatch 方法触发,使用:
store.dispatch('increment')
dispatch 异步操作 this.$store.dispatch('actions的方法',arg)
commit 同步操作 this.$store.commit('mutations的方法',arg)
5、modules: 模块化Vuex
背景:在Vue中State使用是单一状态树结构,应该的所有的状态都放在state里面,如果项目比较复杂,那state是一个很大的对象,store对象也将对变得非常大,难于管理。
module:每个模块拥有自己的 state、mutation、action、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 的状态
同步&异步的理解
为什么要用Action管理异步操作
需求如下
state中存储了一个状态count
现在有两个异步操作,他们都能改变count的值
第二个异步操作的条件依赖第一个异步操作的结果,比如第一个异步操作执行了count ++ ,count :0 = >1,第二个异步操作会先判断当前count的值,if(count === 1) { do something...} else { do something... }
当异步操作涉及互相依赖的情况的时候,我们肯定希望被依赖的操作执行完成之后,再执行依赖项,这样能保证程序执行得到正确的结果,但异步操作,如接口访问这种,往往是不能确定执行完成的时间的,
通常你在接口A中得到一个值,a
接口B需要使用这个值结合B接口返回的值进行一些判断操作,if(a&&b){ ... }
这个时候如果B接口执行完毕了,A接口的值还没过来的话,就可能得到错误的结果。 a => undefined
所以这里牵扯到了一个异步操作的顺序执行问题,既然是异步操作问题,基本都会用到ES6的promise函数去解决,有兴趣的可以用看一下文章——关于promise的一些使用和原理。
ES6语法(3)—— 用promise()对象优雅的解决异步操作
结合async/await的异步函数
asyn function(){
let f = await f1()
....
}
是一个会等待f1执行完再继续的异步,保证了有依赖关系的时候数据的正确性。
若是两个函数之间有依赖关系,比如必须要等f1执行完了再执行f2
有些要等请求发完了再执行的操作就可以使用
DoSomething({commit, state}, n) {
return new Promise(async (resolve, reject) => {
let n = .....
let res = await DoSomething(n)
if (res.operateCode === 'S') {
commit(DO_OTHER_THING, n)
dispatch('OTHER_THING')
//.....
} else {
console.log('error')
}
})