认识Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。其作用是将一些重复性的状态放到一起统一管理,方便后期维护。假如你所负责的项目很小,只有个别几个功能,推荐不使用vuex。反之如果项目庞大,逻辑复杂,这时候就会出现一些状态在多个地方同事出现并复用的情况,这种情况下推荐使用vuex将他们统一管理,后期维护只需要更改vuex管理仓库即可。
由图可见,在Vuex这个闭环中改变其中的数据需要走一个循环。
Vuex项目构建
首先,在src目录下新建store文件夹,再在store文件夹下新建index.js文件,在index.js文件中引入Vuex,然后将它挂载在项目main.js上,供全局调用。
安装:npm install vuex --save
// index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// main.js
import Vue from 'vue'
import store from './store'
new Vue({
store, //全局新建store实例
render: h => h(App)
}).$mount('#app')
Vuex属性介绍
创建store,store就相当于this,有且只有一个,vuex这个插件其实就是一个store对象,vue可以通过this.$store来调用vuex的属性与数值,这是vuex的核心。
// index.js
const store = new Vuex.Store({...}) //内部为其五大属性
尤其可见,store为新建的Vuex实例,接下来就来讲他内部的五大属性:
- state:Vuex定义数据并存放
- getters:相当于state的计算属性,从state派生出一些状态
- mutations:更改store中的状态唯一方法就是提交mutation,它是同步的(如果需要异步可使用actions来调用commit(’
mutation对应方法名
’)这种方法来搭配使用),每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler),回调函数就是我们更改的数据,例如:state,state便作为第一个参数,可传载荷作为第二个参数。 - actions:Action 类似于 mutation,不同在于:
action 提交的是 mutation,而不是直接变更状态;
action可以进行任何异步操作,mutation不可以; - modules:模块化管理,有多个state可以通过模块化import到index.js的module属性中,可直接进行调用。参考vue的组件化思想。
注意:store 中存储的状态是响应式的,当组件从store中读取状态时,如果store中的状态发生了改变,那么相应的组件也会得到更新;
Vuex五大属性使用
首先看一下完整的store实例结构
// index.js
import Vue from 'vue'
import Vuex from 'vuex'
import timepiece from './lw/timepiece.js'
Vue.use(Vuex)
const store = new Vuex.Store({
state: { ... },//定义状态,存放状态
mutations: { ... },// 事件存放位置,更改数据状态,同步操作
actions: { ... },// 修改mutations事件,异步操作
getters: { ... },// state计算属性
// 引入实例模块
modules: { timepiece } //结构与这里的store实例一模一样,只是把逻辑都写到timepiece实例里面
})
export default store; //暴露,供外部访问
state
Vuex 使用单一状态树,它的一个对象就对应全部应用层级状态,即五大属性。所以每个对象也会享受一个store实例。
如何在Vue中获得Vuex状态
Vuex是响应式状态存储的,每当state对应发生变化,就会重新响应层级状态。如下:
// 创建一个 Counter 组件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count // count为state创建的状态
}
}
}
每当store.state.count
发生改变,都会重新求取计算属性,并且触发更新相关联的 DOM。
前面已经讲到将store实例挂载在main上,这样的话就不需要在组件中频繁地导入
挂载之后组件调用方法:
- this.$store
- mapState 辅助函数
当我们要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。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
}
})
}
当映射的计算属性的名称与 state 的子节点名称相同时,可以给 mapState 传一个字符串数组
computed: mapState([
// 映射 this.count 为 store.state.count
'count' // count为state创建的状态
])
mapState 函数返回的是一个对象,为了将它里面的计算属性与组件本身的局部计算属性组合起来,需要用到对象展开运算符:
import { mapState } from 'vuex'
export default{
computed: {
...mapState(['count']), // count为state创建的状态
}
}
Getter
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。它可以帮助我们去从 store 的 state中派生出一些状态,例如:对列表进行过滤并计数:
computed: {
doneTodosCount () { //state中的某个状态
return this.$store.state.todos.filter(todo => todo.done).length //被返回的处理后的值
}
}
组件访问方法:
- 通过属性访问:
Getter暴露 store.getters 对象,直接用它去访问
store.getters.doneTodos // doneTodos为getters中的状态
前面提到可以携带第二个参数,那么我们把其它getters作为第二个参数,也是可以的:
getters: { // getters区间
doneTodos:(state) => { return ... }, //状态doneTodos
doneTodosCount: (state, getters) => { //getters作为第二个参数
return getters.doneTodos.length //doneTodosCount中可以拿到getters中的其它状态 doneTodos
}
}
Vue组件中访问:
computed:{
doneTodosCount(){
return this.$store.getters.doneTodosCount
}
}
注意,getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的
- 通过方法访问
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
- mapGetters 辅助函数
mapGetters与mapState用法相同
import { mapGetters } from 'vuex'
export default{
computed: {
...mapGetters(['doneTodosCount']) //doneTodosCount为getters中的状态
// 或者
...mapGetters({doneCount: 'doneTodosCount'}) //等同于上面,结果重复赋值到doneCount上
}
}
Mutations
mutations是直接操作state中数据的方法的集合,例如对数据的增、删、改等等
const store = new Vuex.Store({
state: {
count:1,
},
mutations:{
// 定义一个事件名 increment
increment(state) { //state指向state里面的所有
// 操作逻辑
state.count++
}
}
})
关于mutations方法调用,假设事件名为 increment
store.commit('increment') //actions内部就是这样调用
mutations方法调用的时候可以提交载荷(payload),就是额外传输一个参数,参数可以为数组、字符串、对象等,称为对象方式分发
载荷—int类型:
mutations: {
increment(state,n) { //这是接收
state.count += n
}
}
store.commit('increment',10) //这是传值
载荷 / 对象分发:
mutations: {
increment(state,payload) { //这是接收
state.count += payload.amount
}
}
store.commit('increment',{amount: 10}) //这是传值---载荷
// 或者
store.commit({
type:'increment',
amount: 10 //这是传值---对象分发
})
Mutations一样可以调用mapMutations辅助函数的方法简写
import { mapState, mapGetters, mapMutations } from 'vuex'
export default {
computed: {
...mapState(['...']), //括号中...为事件名
或者
...mapState({ count: state=>state.count }),
...mapGetters(['...']) //括号中...为事件名
},
methods: {
...mapMutations([ '...' ]) //括号中...为事件名
}
}
Action
综上可知,action与mutation相似,但action包含任何异步操作,并且不能直接修改state,所以只能通过调用mutations的事件名来修改
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
increment(context) { //context就是当前的vuex实例对象;它拥有实例的所有方法
context.commit('increment')
}
或者
increment({commit}) { commit('increment') }
}
})
关于actions方法调用,假设事件名为 increment
store.dispatch('increment')
actions 同样支持载荷方式和对象方法分发:
载荷 / 对象分发:
actions: {
incrementAsync({ commit }) {
setTimeout(()=> {
commit('increment')
},1000) //这就是mutations与actions的区别---异步
}
}
// 载荷
store.dispatch('incrementAsync', { amout: 10 })
// 对象分发
store.dispatch({
type:'incrementAsync',
amout: 10
})
actions一样可以调用mapActions辅助函数的方法简写
import { mapActions } from 'vuex'
export default {
methods: {
...mapActions(['...']), // 括号中的...为方法名
...mapActions({ add:'...' }) // this.$store.dispatch('...')
}
}
组合 actions
有时项目需要在actions内部A调用B,如何解决这一问题呢?首先,store.dispatch处理函数返回的Promise并返回Promise
actions:{
actionA({commit}) {
return new Promise((resolve,reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
},1000)
})
}
}
// 另一个actions
actions: {
actionA({commit}) { ... }, //如上
actionB({ dispatch,commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
store.dispatch('actionA').then(() => { ... }) //另外组件中可以这样去操作
async / await 组合 actions
// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA({ commit }) {
commit('gotData', await getData())
},
async actionB({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。
Modules
模块化管理,用法如下:
// 新建timepiece.js文件,命名随意
const timepiece = {
state: { count: 0 },
mutations: { ... },
actions: { ... },
getters: { ... },
}
export default timepiece
// index.js
import timepiece from './lw/timepiece.js' //路径
const store = new Vuex.Store({
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... },
modules: { timepiece } // 引进
})
假设组件要调用
state
this.$store.state.timepiece.count
Getters
this.$store.getters.count
Mutations
this.$store.commit('start')
Action
this.$store.dispatch('start')
本文是针对vuex官网总结的一些个人心得。至此,总结结束,希望对大家有帮助,后期会根据vuex做一个或多个项目作为练习,以巩固知识。