共计 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.state
和context.getters
来获取 state
和getters
,利用 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
宰割成模块,每个模块领有本人的state
、mutation
、action
、getter
、甚至是嵌套子模块,从上至下进行同样形式的宰割。
8.1,筹备工作
在 store 目录下新建 Modules
文件夹,在 Modules
文件夹中新建modulesA.js
,modulesB.js
,如下图
在 modulesA.js 中写上部分模块的state
、mutation
、action
、getter
,并导出
const moduleA = {state: () => ({a: '我是 moduleA'}),
getters: {},
mutations: {},
actions: {}}
export default moduleA
而后在 store
的index.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
,承受的参数state
和getters
是模块的部分状态对象,而根节点的状态会作为第三个参数 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,命名空间
默认状况下,模块外部的 action
、mutation
和getter
是注册在全局命名空间的,这样使得多个模块可能对同一 mutation
或action
作出响应。如果心愿模块具备更高的封装度和复用性,能够通过给模块增加 namespaced: true
的形式使其成为带命名空间的模块。当模块被注册后,它的所有 getter
、action
及mutation
都会主动依据模块注册的门路调整命名。
8.6.1,应用
先在模块 moduleB.js
中增加namespaced: true
const moduleB = {
namespaced: true,
state: () => ({b: '我是 moduleB'}),
mutations: {},
actions: {},
getters: {}}
export default moduleB
在 store
的index.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
, mapMutations
,mapActions
用法一样。
<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,在带命名空间的模块中内拜访全局内容
如果你心愿应用全局的 state
和getter
,rootState
和 rootGetters
会作为第三和第四参数传入 getter
,也会通过context
对象的属性传入 action
。若须要在全局命名空间内散发action
或提交 mutation
,将{root: true}
作为第三参数传给 dispatch
或commit
即可
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
- 简书
- 博客园
- 掘金