关于vue.js:超详细Vuex手把手教程

38次阅读

共计 10013 个字符,预计需要花费 26 分钟才能阅读完成。

1,前言

最近在重温 vue 全家桶,再看一遍感觉记忆更粗浅,所以专门记录一下(本文 vuex 版本为 v3.x)。

2,Vuex 是什么

Vuex 是专为 Vue.js 开发的状态管理模式。它采纳集中式存储,治理所有组件的状态,并以相应的规定保障状态以一种可预测的形式发生变化(我的了解就是全局变量)。

3,5 大属性阐明

state

对象类型,相似于实例的 data 属性,存放数据

getters

对象类型,相似于实例的计算属性 computed

mutations

对象类型,相似于实例的 methods,然而不能解决异步办法

actions

对象类型,相似于实例的 methods,能够解决异步办法

modules

对象类型,当 state 内容比拟多时,通过该属性宰割成小模块,每个模块都领有本人的 state、mutation、action、getter

4,state

存储在 state 中的数据和 Vue 实例中的 data 遵循雷同的规定,必须是纯正的对象。

4.1 间接拜访

this.$store.state.xxx

4.1 应用 mapState 映射

<template>
    <div id="communication">
        <p> 计数:{{getCount}}</p>
        <p> 学校:{{getSchool('我是参数') }}</p>
    </div>
</template>

<script>
import {mapState} from 'vuex'

export default {
    name: 'Vuex',
    data() {
        return {date: 1998}
    },
    computed: {
        ...mapState({
            // mapState 默认会把 state 当第一个参数传进来
            getCount: state => state.count,
            getSchool(state) {return (val) => {return state.school + val + this.date}
            }
        })
    },
    mounted() {
        // 间接取值
        console.log(this.$store.state.count)
    }
}
</script>

5,getters

getter的返回值会依据它的依赖被缓存起来,且只有当它的依赖值产生了扭转才会被从新计算,并且默认承受 state 作为其第一个参数,也能够承受其余 getter 作为第二个参数(如下例)

5.1 先在 vuex 中定义 getters

export default new Vuex.Store({
    state: {
        count: 0,
        school: '清华大学'
    },
    getters: {
        // 返回解决后的 state 值
        getValue(state) {return state.count + '!'},
        // 返回调用本身 getters 解决后的 state 值
        getGetters(state, getters) {return state.school + getters.getValue},
        // 承受内部传参后处理的值(在通过办法拜访时,每次都会去进行调用,而不会缓存后果)getParam(state) {return (param) => {return state.school + param}
        }
    },
    mutations: {},
    actions: {},
    modules: {}})

5.2 间接获取值

// 取值
console.log(this.$store.getters.getGetters)
// 传参取值
console.log(this.$store.getters.getParam('param'))

5.3 应用 mapGetters 映射

<template>
    <div id="communication">
        <p> 计数:{{getGetters}}</p>
        <p> 学校:{{getParam(date) }}</p>
    </div>
</template>

<script>
import {mapGetters} from 'vuex'

export default {
    name: 'Vuex',
    data() {
        return {date: 1998}
    },
    computed: {
        ...mapGetters([
            'getGetters',
            'getParam'
        ])
    },
    mounted() {
        // 间接取值
        console.log(this.$store.getters.getGetters)
        console.log(this.getParam(this.date))
    }
}
</script>

6,Mutation

通过调用 this.$store.commit('xxx'),调用mutation 中的办法,更改 store 中的值

6.1,先在 mutations 中注册事件

export default new Vuex.Store({
    state: {
        count: 0,
        school: '清华大学'
    },
    getters: {},
    mutations: {
        // 默认 state 作为第一个参数
        handleAdd(state) {state.count++},
        // 承受传参
        handleChange(state, value) {state.school = value}
    },
    actions: {},
    modules: {}})

6.2,在组件中调用办法 commit 批改值

<template>
    <div id="communication">
        <p> 计数:{{count}}</p>
        <el-button @click="handleStoreAdd"> 减少 </el-button>
        <el-button @click="handleStoreChange"> 传参 </el-button>
    </div>
</template>

<script>
import {mapState} from 'vuex'

export default {
    name: 'Vuex',
    data() {
        return {school: '武汉大学'}
    },
    computed: {
        ...mapState(['count'])
    },
    methods: {
        // 调用批改
        handleStoreAdd() {this.$store.commit('handleAdd')
        },
        // 传递参数批改
        handleStoreChange() {this.$store.commit('handleChange', this.school)
        }
    }
}
</script>

6.3,应用常量定义方法名

新建文件mutation-types.js,定义方法名的常量,并导出

export const ADD_COUNT = 'ADD_COUNT'
export const CHANGE = 'CHANGE'

在 store 中

import Vue from 'vue'
import Vuex from 'vuex'
import * as MT from './mutation-types'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        count: 0,
        school: '清华大学'
    },
    getters: {},
    mutations: {
        // 默认 state 作为第一个参数
        [MT.ADD_COUNT](state) {state.count++},
        // 承受传参
        [MT.CHANGE](state, value) {state.school = value}
    },
    actions: {},
    modules: {}})

在组件中

<template>
    <div id="communication">
        <p> 计数:{{count}}</p>
        <el-button @click="handleStoreAdd"> 减少 </el-button>
        <el-button @click="handleStoreChange"> 传参 </el-button>
    </div>
</template>

<script>
import {mapState} from 'vuex'
import * as MT from '../../store/mutation-types'
export default {
    name: 'Vuex',
    data() {
        return {school: '武汉大学'}
    },
    computed: {
        ...mapState(['count'])
    },
    methods: {
        // 调用批改
        handleStoreAdd() {this.$store.commit(MT.ADD_COUNT)
        },
        // 传递参数批改
        handleStoreChange() {this.$store.commit(MT.CHANGE, this.school)
        }
    }
}
</script>

6.4,应用 mapMutations 映射

<template>
    <div id="communication">
        <p> 计数:{{count}}</p>
        <p> 计数:{{school}}</p>
        <el-button @click="handleStoreAdd"> 减少 </el-button>
        <el-button @click="handleStoreChange(schools)"> 传参 </el-button>
    </div>
</template>

<script>
import {mapState, mapMutations} from 'vuex'
import * as MT from '../../store/mutation-types'

export default {
    name: 'Vuex',
    data() {
        return {schools: '武汉大学'}
    },
    computed: {
        ...mapState([
            'count',
            'school'
        ])
    },
    methods: {
        ...mapMutations({
            handleStoreAdd: MT.ADD_COUNT,
            handleStoreChange: MT.CHANGE
        })
    }
}
</script>

7,Action

留神,Action提交的是mutation,而不是间接变更状态,并且能够蕴含任意异步操作

7.1,在 store 中定义

import Vue from 'vue'
import Vuex from 'vuex'
import * as MT from './mutation-types'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        count: 0,
        school: '清华大学'
    },
    getters: {},
    mutations: {
        // 默认 state 作为第一个参数
        [MT.ADD_COUNT](state) {state.count++},
        // 承受传参
        [MT.CHANGE](state, value) {state.school = value}
    },
    actions: {add(context) {context.commit(MT.ADD_COUNT)
        }
    },
    modules: {}})

7.2,在组件中应用

<template>
    <div id="communication">
        <p> 计数:{{count}}</p>
        <el-button @click="actionAdd"> 减少 </el-button>
    </div>
</template>

<script>
import {mapState, mapMutations} from 'vuex'
import * as MT from '../../store/mutation-types'

export default {
    name: 'Vuex',
    data() {
        return {schools: '武汉大学'}
    },
    computed: {
        ...mapState([
            'count',
            'school'
        ])
    },
    methods: {
        ...mapMutations({
            handleStoreAdd: MT.ADD_COUNT,
            handleStoreChange: MT.CHANGE
        }),
        // 调用 action 的办法,须要应用 $store.dispatch
        actionAdd() {this.$store.dispatch('add')
        }
    }
}
</script>

7.3,应用 mapActions 映射

import {mapActions} from 'vuex'

methods: {
    ...mapActions(['moduleFn'])
}

或者

import {mapActions} from 'vuex'

methods: {
    ...mapActions([fn: 'moduleFn'])
}

7.4,简化写法

Action承受一个与 store 实例具备雷同办法和属性的 context 参数对象,因而你能够调用 context.commit 提交一个 mutation,或者通过context.statecontext.getters来获取 stategetters,利用 ES6 的解构,能够简化写法。

actions: {add({ commit, state}) {commit(MT.CHANGE, state.school)
  }
}

7.5,执行异步操作

在 vuex 中

import Vue from 'vue'
import Vuex from 'vuex'
import * as MT from './mutation-types'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {count: 0},
    getters: {},
    mutations: {
        // 默认 state 作为第一个参数
        [MT.ADD_COUNT](state) {state.count++}
    },
    actions: {add({ commit}) {return new Promise((resolve, reject) => {setTimeout(() => {commit(MT.ADD_COUNT)
                    resolve()}, 1000)
            })
        }
    },
    modules: {}})

在组件中应用 async / await 或者 then / catch 解决异步

<template>
    <div id="communication">
        <p> 计数:{{count}}</p>
        <el-button @click="actionAdd"> 减少 </el-button>
    </div>
</template>

<script>
import {mapState, mapMutations} from 'vuex'
import * as MT from '../../store/mutation-types'

export default {
    name: 'Vuex',
    data() {
        return {schools: '武汉大学'}
    },
    computed: {
        ...mapState([
            'count',
            'school'
        ])
    },
    methods: {
        ...mapMutations({
            handleStoreAdd: MT.ADD_COUNT,
            handleStoreChange: MT.CHANGE
        }),
        // 调用 action 的办法,须要应用 $store.dispatch
        async actionAdd() {await this.$store.dispatch('add')
            console.log(1998)
        }
    }
}
</script>

8,Modules

当利用变得非常复杂时,store对象就可能变得相当臃肿。这时候能够将 store 宰割成模块,每个模块领有本人的statemutationactiongetter、甚至是嵌套子模块,从上至下进行同样形式的宰割。

8.1,筹备工作

在 store 目录下新建 Modules 文件夹,在 Modules 文件夹中新建modulesA.jsmodulesB.js,如下图

在 modulesA.js 中写上部分模块的statemutationactiongetter,并导出

const moduleA = {state: () => ({a: '我是 moduleA'}),
    getters: {},
    mutations: {},
    actions: {}}

export default moduleA

而后在 storeindex.js中引入,并丢进 modules 对象里

import Vue from 'vue'
import Vuex from 'vuex'
import * as MT from './mutation-types'
import moduleA from './modules/moduleA'
import moduleB from './modules/moduleB'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {count: 0},
    getters: {},
    mutations: {},
    actions: {},
    modules: {
        moduleA,
        moduleB
    }
})

8.2,应用 modules 中注入的模块的 state

在组件中间接应用

this.$store.state.moduleA.xxx

在组件中应用 mapState 映射

<span>{{moduleA.xxx}}</span>

import {mapState} from 'vuex'

computed: {
    ...mapState(['moduleA'])
}

8.3,应用 modules 中注入模块的 getters

在组件中间接应用

this.$store.getters.getModuleA

在组件中应用 mapState 映射

<p>{{getModuleA}}</p>

import {mapGetters} from 'vuex'

computed: {
    ...mapGetters(['getModuleA'])
}

模块外部的 getter,承受的参数stategetters是模块的部分状态对象,而根节点的状态会作为第三个参数 rootState 裸露进去

const moduleA = {
    getters: {getModuleA(state, getters, rootState) {return state.xxx + '---' + rootState.xxx}
    }
}

如果须要带参数

const moduleA = {
    getters: {getModuleA(state, getters, rootState) {return (value) => {return state.a + '---' + value}
        }
    }
}

8.4,应用 modules 中注入模块的 mutations

在组件中间接应用

this.$store.commit('setModuleA') || this.$store.commit('setModuleA', '参数')

在组件中应用 mapMutations 映射

import {mapMutations} from 'vuex'

methods: {
    ...mapMutations([openFn: 'setModuleA'])
}

模块外部的 mutations,默认承受的第一个参数state 是模块的部分状态对象

const moduleA = {
    mutations: {setModuleA(state) {state.xxx += 'xxx'}
    }
}

如果须要带参数

const moduleA = {
    mutations: {setModuleA(state, value) {state.xxx += value}
    }
}

8.5,应用 modules 中注入模块的 actions

在组件中间接应用

this.$store.dispatch('xxx')

在组件中应用 mapActions 映射

import {mapActions} from 'vuex'

methods: {
    ...mapActions(['moduleA'])
}

或者重命名

import {mapActions} from 'vuex'

methods: {
    ...mapActions({fn: 'moduleA'})
}

对于模块外部的 action,部分状态通过context.state 裸露进去,根节点状态则为context.rootState

const moduleA = {
  // ...
  actions: {fn ({ state, commit, rootState}) {if ((state.count + rootState.count) % 2 === 1) {commit('increment')
      }
    }
  }
}

8.6,命名空间

默认状况下,模块外部的 actionmutationgetter是注册在全局命名空间的,这样使得多个模块可能对同一 mutationaction作出响应。如果心愿模块具备更高的封装度和复用性,能够通过给模块增加 namespaced: true 的形式使其成为带命名空间的模块。当模块被注册后,它的所有 getteractionmutation都会主动依据模块注册的门路调整命名。

8.6.1,应用

先在模块 moduleB.js 中增加namespaced: true

const moduleB = {
    namespaced: true,
    state: () => ({b: '我是 moduleB'}),
    mutations: {},
    actions: {},
    getters: {}}

export default moduleB

storeindex.js

import moduleA from './modules/moduleA'
import moduleB from './modules/moduleB'

export default new Vuex.Store({state: {},
    getters: {},
    mutations: {},
    actions: {},
    modules: {
        moduleA,
        moduleB
    }
})

如果在组件中应用命名空间,须要带上空间名称,mapState, mapGetters, mapMutationsmapActions用法一样。

<script>
import {mapState, mapGetters, mapMutations} from 'vuex'

export default {
    name: 'Vuex',
    data() {return {}
    },
    computed: {
        // 此处注入的是 moduleA 模块的数据
        ...mapState('moduleA', ['a']),
        // 须要注入 moduleB 模块,就再写一个
        ...mapState('moduleB', ['b'])
    },
    mounted() {
        // 间接应用
        console.log(this.$store.state.moduleA.a)
        console.log(this.$store.state.moduleB.b)
    },
    methods: {}}
</script>

8.6.2,在带命名空间的模块中内拜访全局内容

如果你心愿应用全局的 stategetterrootStaterootGetters 会作为第三和第四参数传入 getter,也会通过context 对象的属性传入 action。若须要在全局命名空间内散发action 或提交 mutation,将{root: true} 作为第三参数传给 dispatchcommit即可

const moduleA = {
    namespaced: true,
    state: () => ({a: '我是 moduleA'}),
    getters: {getModuleA(state, getters, rootState, rootGetters) {
            // 应用全局命名空间的 state 或 getters
            return state.a + rootState.count
        }
    },
    mutations: {setModuleA(state) {console.log(state.a)
        }
    },
    actions: {addM({ state, commit, dispatch, rootState, rootGetters}) {console.log(rootState)
            console.log(rootGetters)
            // 调用全局命名空间的办法
            dispatch('rootFunction', null, { root: true})
        }
    }
}

export default moduleA

8.6.3,在带命名空间的模块注册全局 action

在带命名空间的模块注册全局 action,须要增加root: true,并将这个action 的定义放在函数 handler 中,其中,handler 的第一个参数 namespacedContext 就是 action 中的 Context 参数

const moduleA = {
    namespaced: true,
    state: () => ({a: '我是 moduleA'}),
    getters: {},
    mutations: {},
    actions: {
        rootFn: {
            root: true,
            handler(namespacedContext, param) {console.log(namespacedContext.state)
            }
        }
    }
}

export default moduleA

如果看了感觉有帮忙的,我是 @鹏多多,欢送 点赞 关注 评论;END

往期文章

  • 应用 nvm 治理 node.js 版本以及更换 npm 淘宝镜像源

个人主页

  • CSDN
  • GitHub
  • 简书
  • 博客园
  • 掘金

正文完
 0