vuex的基础使用整理
Vuex整理
Vuex的产生的原因:
一般的项目中的数据与视图的关系都是“单向数据流”
即:state在view中展示、action进行state中的状态变化、view中用户进行操作触发action
当多个组件共享同一个状态的时候,单向数据流的简洁模式就会被破坏。可以使用下边的方式解决这些问题
常规的组件传值的方式:
当组件的嵌套关系不是很复杂的时候可以使用常规的传值实现状态的共享。
之前有过记录https://www.cnblogs.com/wyongz/p/11425433.html
Vue总线机制
vue中复杂嵌套组件之间通信除了使用vuex,也可以通过bus总线,两者适用场景不同。
bus适合小项目、数据被更少组件使用的项目,对于中大型项目 数据在很多组件之间使用的情况 bus就不太适用了。bus其实就是一个发布订阅模式,利用vue的自定义事件机制,在触发的地方通过$emit向外发布一个事件,在需要监听的页面,通过$on监听事件。
vuex适用中大型项目、数据在多组件之间公用的情况。
这种方法的原理是通过一个空的Vue实例作为中央事件总线,通过它来触发事件和监听事件,可以实现几乎任何组件间的通信
在utils文件夹下面定义一个bus.js,这样一个中央事件总线就定好了,里面内容就这两行:
import Vue from 'vue' export const Event = new Vue() // 定义一个事件总线
传递方:
import { Event } from '../../utils/bus' // 引入这个事件总线 export default { data() { userId: '' } watch: { userId() { // 监听userId改变时触发事件,当然你可以在所有其他事件去触发$emit Event.$emit('changeUser', userId) // $emit接收参数,一个是触发的事件名,一个是携带的参数 } }
接收方:
import { Event } from '../../utils/bus' // 依然是把bus总线引进来 export default { created() { Event.$on('changeSonRoute',showIndex => { // $on接收发送方传给我们的数据 console.log(showIndex) }) }
综上所述,在大项目中组件嵌套比较复杂的情况下的状态共享使用vuex比较合适
Vuex的使用
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
首先引入vuex并使用
import Vuex from 'vuex'
Vue.use(Vuex)
创建实例
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } })
注入vue中,这样就可以通过实例调用了this.$store
new Vue({ el: '#app', store, render: h => h(App) })
一般规范是State中存放状态,multation中修改状态,action中存放业务逻辑
State
改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化
子组件中访问state
上边通过在根实例中注册 store
选项, store 实例会注入到根组件下的所有子组件中,所以子组件能通过 this.$store
访问到
由于 Vuex 的状态存储是响应式的,使用状态最简单的方法就是在计算属性中返回某个状态,当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 } }) }
三种写法:
1、 箭头函数
2、 直接写状态中的名称(字符串形式)
3、 如果需要vuex中的状态与组件中的状态进行计算属性的要使用常规函数
Vuex中的状态与组件中的计算属性混合使用
因为mapState是对象形式,可以使用解构的方式获取对象中的属性
computed: { localComputed () { /* ... */ }, // 使用对象展开运算符将此对象混入到外部对象中 ...mapState({ // ... }) }
Getter
Vuex中的getter相当于组件中的计算属性,从 store 中的 state 中派生出一些状态
Getter用法:
Getter 接受 state 作为其第一个参数
getters: { doneTodos: state => { return state.todos.filter(todo => todo.done) } }
如果返回的是属性的话使用方式和state相同,但是如果需要外部传值的就要返回方法并在使用的时候传入需要的参数
属性
computed: { doneTodosCount () { return this.$store.getters.doneTodosCount } }
方法
getters: { // ... getTodoById: (state) => (id) => { return state.todos.find(todo => todo.id === id) } } store.getters.getTodoById(2)
这种方式有点类似于react中的高阶函数
mapGetters 辅助函数
import { mapGetters } from 'vuex' export default { // ... computed: { // 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters([ 'doneTodosCount', 'anotherGetter', doneCount: 'doneTodosCount'//不使用getter中的名字另取名字的写法 // ... ]) } }
Mutation和action的区别
下边涉及到mutation和action,所以在这插入一下两者之间的区别
1、流程顺序
“相应视图—>修改State”拆分成两部分,视图触发Action,Action再触发Mutation。
2、角色定位
基于流程顺序,二者扮演不同的角色。
Mutation:专注于修改State,理论上是修改State的唯一途径。
Action:业务代码、异步请求。
3、限制
角色不同,二者有不同的限制。
Mutation:必须同步执行。
Action:可以异步,但不能直接操作State
Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
定义: mutations: { increment (state) { // 变更状态 state.count++ } } 调用: store.commit('increment')
向 store.commit
传入额外的参数
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)
参数应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读
对象形式的调用:
store.commit({ type: 'increment', amount: 10 })
Mutation 需遵守 Vue 的响应规则
最好提前在你的 store 中初始化好所有所需属性。
当需要在对象上添加新属性时,你应该
使用 Vue.set(obj, 'newProp', 123), 或者以新对象替换老对象
在组件中需要多个提交 Mutation
import { mapMutations } from 'vuex' export default { // ... methods: { ...mapMutations([ 'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')` // `mapMutations` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')` }) } }
Action
Action中处理业务逻辑,来使mutation中保持只进行state的状态修改操作
而且mutation中不能执行异步的操作,可以在action中执行异步操作
你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用
import { mapActions } from 'vuex' export default { // ... methods: { ...mapActions([ 'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')` // `mapActions` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)` ]), ...mapActions({ add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')` }) } }
组合 Action
Action 通常是异步的,那么如何知道 action 什么时候结束呢?更重要的是,我们如何才能组合多个 action,以处理更加复杂的异步流程?
store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise:
actions: { actionA ({ commit }) { return new Promise((resolve, reject) => { setTimeout(() => { commit('someMutation') resolve() }, 1000) }) } }
现在你可以:
store.dispatch('actionA').then(() => {
// ...
})
在另外一个 action 中也可以:
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
最后,如果我们利用 async / await,我们可以如下组合 action:最典型的使用就是用户权限信息的判断部分,等后台返回数据后再进行判断来使用vuex来存储登录信息
// 假设 getData() 和 getOtherData() 返回的是 Promise actions: { async actionA ({ commit }) { commit('gotData', await getData()) }, async actionB ({ dispatch, commit }) { await dispatch('actionA') // 等待 actionA 完成 commit('gotOtherData', await getOtherData()) } }