1,前言
最近在重溫vue全家桶,再看一遍感覺記憶更深刻,所以專門記錄一下(本文vuex版本為v3.x)。
2,Vuex 是什么
Vuex是專為Vue.js開發(fā)的狀態(tài)管理模式。它采用集中式存儲,管理所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化(我的理解就是全局變量)。
3,5大屬性說明
state
對象類型,類似于實例的 data屬性,存放數(shù)據(jù)
getters
對象類型,類似于實例的計算屬性 computed
mutations
對象類型,類似于實例的 methods,但是不能處理異步方法
actions
對象類型,類似于實例的 methods,可以處理異步方法
modules
對象類型,當state內(nèi)容比較多時,通過該屬性分割成小模塊,每個模塊都擁有自己的 state、mutation、action、getter
4,state
存儲在state中的數(shù)據(jù)和Vue實例中的data遵循相同的規(guī)則,必須是純粹的對象。
4.1 直接訪問
this.$store.state.xxx
4.1 使用mapState映射
<template>
<div id="communication">
<p>計數(shù):{{ getCount }}</p>
<p>學校:{{ getSchool('我是參數(shù)') }}</p>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'Vuex',
data() {
return {
date: 1998
}
},
computed: {
...mapState({
// mapState默認會把state當?shù)谝粋€參數(shù)傳進來
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的返回值會根據(jù)它的依賴被緩存起來,且只有當它的依賴值發(fā)生了改變才會被重新計算,并且默認接受state作為其第一個參數(shù),也可以接受其他getter作為第二個參數(shù)(如下例)
5.1 先在vuex中定義getters
export default new Vuex.Store({
state: {
count: 0,
school: '清華大學'
},
getters: {
// 返回處理后的state值
getValue(state) {
return state.count + '!'
},
// 返回調(diào)用自身getters處理后的state值
getGetters(state, getters) {
return state.school + getters.getValue
},
// 接受外部傳參后處理的值(在通過方法訪問時,每次都會去進行調(diào)用,而不會緩存結(jié)果)
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>計數(shù):{{ 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
通過調(diào)用this.$store.commit('xxx'),調(diào)用mutation中的方法,更改store中的值
6.1,先在mutations中注冊事件
export default new Vuex.Store({
state: {
count: 0,
school: '清華大學'
},
getters: {},
mutations: {
// 默認state作為第一個參數(shù)
handleAdd(state) {
state.count++
},
// 接受傳參
handleChange(state, value) {
state.school = value
}
},
actions: {},
modules: {}
})
6.2,在組件中調(diào)用方法commit修改值
<template>
<div id="communication">
<p>計數(shù):{{ 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: {
// 調(diào)用修改
handleStoreAdd() {
this.$store.commit('handleAdd')
},
// 傳遞參數(shù)修改
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作為第一個參數(shù)
[MT.ADD_COUNT](state) {
state.count++
},
// 接受傳參
[MT.CHANGE](state, value) {
state.school = value
}
},
actions: {},
modules: {}
})
在組件中
<template>
<div id="communication">
<p>計數(shù):{{ 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: {
// 調(diào)用修改
handleStoreAdd() {
this.$store.commit(MT.ADD_COUNT)
},
// 傳遞參數(shù)修改
handleStoreChange() {
this.$store.commit(MT.CHANGE, this.school)
}
}
}
</script>
6.4,使用mapMutations映射
<template>
<div id="communication">
<p>計數(shù):{{ count }}</p>
<p>計數(shù):{{ 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,而不是直接變更狀態(tài),并且可以包含任意異步操作
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作為第一個參數(shù)
[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>計數(shù):{{ 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
}),
// 調(diào)用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參數(shù)對象,因此你可以調(diào)用context.commit提交一個mutation,或者通過context.state和context.getters來獲取state和getters,利用ES6的解構(gòu),可以簡化寫法。
actions: {
add({ commit, state }) {
commit(MT.CHANGE, state.school)
}
}
7.5,執(zhí)行異步操作
在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作為第一個參數(shù)
[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>計數(shù):{{ 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
}),
// 調(diào)用action的方法,需要使用$store.dispatch
async actionAdd() {
await this.$store.dispatch('add')
console.log(1998)
}
}
}
</script>
8,Modules
當應(yīng)用變得非常復雜時,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'
])
}
模塊內(nèi)部的getter,接受的參數(shù)state和getters是模塊的局部狀態(tài)對象,而根節(jié)點的狀態(tài)會作為第三個參數(shù)rootState暴露出來
const moduleA = {
getters: {
getModuleA(state, getters, rootState) {
return state.xxx + '---' + rootState.xxx
}
}
}
如果需要帶參數(shù)
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', '參數(shù)')
在組件中使用mapMutations映射
import { mapMutations } from 'vuex'
methods: {
...mapMutations([
openFn: 'setModuleA'
])
}
模塊內(nèi)部的mutations,默認接受的第一個參數(shù)state是模塊的局部狀態(tài)對象
const moduleA = {
mutations: {
setModuleA(state) {
state.xxx += 'xxx'
}
}
}
如果需要帶參數(shù)
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'
})
}
對于模塊內(nèi)部的action,局部狀態(tài)通過context.state暴露出來,根節(jié)點狀態(tài)則為context.rootState
const moduleA = {
// ...
actions: {
fn ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
8.6,命名空間
默認情況下,模塊內(nèi)部的action、mutation和getter是注冊在全局命名空間的,這樣使得多個模塊能夠?qū)ν?code>mutation或action作出響應(yīng)。如果希望模塊具有更高的封裝度和復用性,可以通過給模塊添加namespaced: true的方式使其成為帶命名空間的模塊。當模塊被注冊后,它的所有getter、action及mutation都會自動根據(jù)模塊注冊的路徑調(diào)整命名。
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模塊的數(shù)據(jù)
...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 ,在帶命名空間的模塊中內(nèi)訪問全局內(nèi)容
如果你希望使用全局的state和getter,rootState和rootGetters會作為第三和第四參數(shù)傳入getter,也會通過context對象的屬性傳入action。若需要在全局命名空間內(nèi)分發(fā)action或提交mutation,將{ root: true }作為第三參數(shù)傳給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)
// 調(diào)用全局命名空間的方法
dispatch('rootFunction', null, { root: true })
}
}
}
export default moduleA
8.6.3,在帶命名空間的模塊注冊全局action
在帶命名空間的模塊注冊全局action,需要添加root: true,并將這個action的定義放在函數(shù)handler中,其中,handler的第一個參數(shù)namespacedContext就是action中的Context參數(shù)
const moduleA = {
namespaced: true,
state: () => ({
a: '我是moduleA'
}),
getters: {},
mutations: {},
actions: {
rootFn: {
root: true,
handler(namespacedContext, param) {
console.log(namespacedContext.state)
}
}
}
}
export default moduleA
本次分享就到這兒啦,我是@鵬多多,如果您看了覺得有幫助,歡迎評論,關(guān)注,點贊,轉(zhuǎn)發(fā),我們下次見~
往期文章
- 細數(shù)JS中實用且強大的操作符&運算符
- 使用nvm管理node.js版本以及更換npm淘寶鏡像源
- vue中利用.env文件存儲全局環(huán)境變量,以及配置vue啟動和打包命令
- 微信小程序?qū)崿F(xiàn)搜索關(guān)鍵詞高亮
個人主頁