Vuex狀態管理——任意組件間通信


核心概念

在Vue中實現集中式狀態(數據)管理的一個Vue插件,對vue應用中多個組件的共享狀態進行集中式的管理(讀/寫),也是一種組件間通信的方式,且適用於任意組件間通信。

vuex

每一個 Vuex 應用的核心就是 store(倉庫)。“store”基本上就是一個容器,它包含着你的應用中大部分的狀態 (state)。Vuex 和單純的全局對象有以下兩點不同:

  1. Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那么相應的組件也會相應地得到高效更新。
  2. 你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地了解我們的應用。

state

vuex狀態管理對象,作為一個“唯一數據源 (SSOT (opens new window))”而存在。組件通過computed計算屬性訪問

 computed: {
    count () {
      return this.$store.state.count
    }

actions

值為一個對象,包含多個響應用戶動作的回調函數;通過commit()來觸發mutation中函數的調用,間接更新state

Action 函數接受一個與 store 實例具有相同方法和屬性的 context 對象,因此你可以調用 context.commit 提交一個 mutation,或者通過 context.statecontext.getters 來獲取 state 和 getters。但不是store本身

mutations

Vuex 中的 mutation 非常類似於事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是我們實際進行狀態更改的地方,並且它會接受 state 作為第一個參數:

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 變更狀態
      state.count++
    }
  }
})
//組件使用
store.commit('increment')

你可以向 store.commit 傳入額外的參數,即 mutation 的 載荷(payload)

// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
store.commit('increment', 10)

getters

Vuex 允許我們在 store 中定義“getter”(可以認為是 store 的計算屬性)。就像計算屬性一樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變才會被重新計算。

Getter 接受 state 作為其第一個參數:

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos(state) {
      return state.todos.filter(todo => todo.done)
    }
  }
})

組件訪問
Getter 會暴露為 store.getters 對象,你可以以屬性的形式訪問這些值:

store.getters.doneTodos 
// [{ id: 1, text: '...', done: true }]

簡單使用

1、安裝

npm install vuex

2、main.js引入

//引入store
import store from './store'
new Vue({
	el:'#app',
	render: h => h(App),
	store
})

3、創建配置文件:src/store/index.js

//該文件用於創建Vuex中最為核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//應用Vuex插件
Vue.use(Vuex)

//准備actions——用於響應組件中的動作
const actions = {
	/* jia(context,value){
		console.log('actions中的jia被調用了')
		context.commit('JIA',value)
	},
	jian(context,value){
		console.log('actions中的jian被調用了')
		context.commit('JIAN',value)
	}, */
	jiaOdd(context,value){
		console.log('actions中的jiaOdd被調用了')
		if(context.state.sum % 2){
			context.commit('JIA',value)
		}
	},
	jiaWait(context,value){
		console.log('actions中的jiaWait被調用了')
		setTimeout(()=>{
			context.commit('JIA',value)
		},500)
	}
}
//准備mutations——用於操作數據(state)
const mutations = {
	JIA(state,value){
		console.log('mutations中的JIA被調用了')
		state.sum += value
	},
	JIAN(state,value){
		console.log('mutations中的JIAN被調用了')
		state.sum -= value
	}
}
//准備state——用於存儲數據
const state = {
	sum:0 //當前的和
}
//准備getters——用於將state中的數據進行加工
const getters = {
	bigSum(state){
		return state.sum*10
	}
}

//創建並暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state,
    getters
})

4、組件使用

<template>
	<div>
		<h1>當前求和為:{{$store.state.sum}}</h1>
		<h3>當前求和放大10倍為:{{$store.getters.bigSum}}</h3>
		<select v-model.number="n">
			<option value="1">1</option>
			<option value="2">2</option>
			<option value="3">3</option>
		</select>
		<button @click="increment">+</button>
		<button @click="decrement">-</button>
		<button @click="incrementOdd">當前求和為奇數再加</button>
		<button @click="incrementWait">等一等再加</button>
	</div>
</template>

<script>
	export default {
		name:'Count',
		data() {
			return {
				n:1, //用戶選擇的數字
			}
		},
		methods: {
			increment(){
             //邏輯簡單的情況下可跳過actionscommit給mutations直接操作state數據
				this.$store.commit('JIA',this.n)
			},
			decrement(){
				this.$store.commit('JIAN',this.n)
			},
			incrementOdd(){
				this.$store.dispatch('jiaOdd',this.n)
			},
			incrementWait(){
				this.$store.dispatch('jiaWait',this.n)
			},
		},
		mounted() {
			console.log('Count',this.$store)
		},
	}
</script>

<style lang="css">
	button{
		margin-left: 5px;
	}
</style>

mapState、mapGetters、mapActions、mapMutations

當一個組件需要獲取多個狀態的時候,將這些狀態都聲明為計算屬性會有些重復和冗余。為了解決這個問題,我們可以使用 mapState 輔助函數幫助我們生成計算屬性

const state = {
	sum:0, //當前的和
	name:'',
	sex:''
}
const getters = {
	bigSum(state){
		return state.sum*10
	}
}
computed:{
//借助mapState生成計算屬性,從state中讀取數據。(數組寫法)
...mapState(['sum','name','sex']),
  //相當於
   sum(){
    return this.$store.state.sum
   } 
   ... 
 //借助mapGetters生成計算屬性,從getters中讀取數據。(數組寫法)
...mapGetters(['bigSum'])
}
methods: {
			
    //借助mapMutations生成對應的方法,方法中會調用commit去聯系mutations(對象寫法)
    ...mapMutations({increment:'JIA',decrement:'JIAN'}),

    //相當於
    increment(){
        this.$store.commit('JIA',this.n)
    },
    decrement(){
        this.$store.commit('JIAN',this.n)
    }, 

    //數組寫法
     ...mapMutations(['JIA','JIAN']),

/* ************************************************* */
   //借助mapActions生成對應的方法,方法中會調用dispatch去聯系actions(對象寫法)
   ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    //相當於
    incrementOdd(){
        this.$store.dispatch('jiaOdd',this.n)
    },
    incrementWait(){
        this.$store.dispatch('jiaWait',this.n)
    }, 
    //數組寫法
    ...mapActions(['jiaOdd','jiaWait'])
},
  • 在計算屬性中定義后,模板中就可以直接訪問變量,不需要再帶上$store.state$store.getters
  • mapActionsmapMutations無法傳參,如果要傳參數,需要在方法調用出定義參數
<template>
    <div>
        <h1>當前求和為:{{sum}}</h1>
        <h3>當前求和放大10倍為:{{bigSum}}</h3>
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <!--在此處傳遞參數-->
        <button @click="increment(n)">+</button>
        <button @click="decrement(n)">-</button>
        <button @click="jiaWait(n)">等一等再加</button>
    </div>
</template>

<script>
    import {mapActions, mapGetters, mapMutations, mapState} from "vuex";

    export default {
        name:'count',
        data() {
            return {
                n:1, //用戶選擇的數字
            }
        },
        methods: {
            ...mapMutations({increment: 'JIA',decrement: 'JIAN'}),
            ...mapActions(['jiaWait']),
        },
        mounted() {
            console.log('Count',this.$store)
        },
        computed:{
            ...mapState(['sum']),
            ...mapGetters(['bigSum'])
        }
    }
</script>

<style lang="css">
    button{
        margin-left: 5px;
    }
</style>

模塊化

如果vuex只有一個state,應用的所有狀態會集中到一個比較大的對象。當應用變得非常復雜時,store 對象就有可能變得相當臃腫。

為了解決以上問題,Vuex 允許我們將 store 分割成模塊(module)。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行同樣方式的分割

配置

在store文件加下創建多個store配置文件,名字自定義

//person.js 人員管理相關的配置
export default {
	namespaced:true,	//命名空間需設置為true
	actions:{
		addPersonWang(context,value){
			if(value.name.indexOf('王') === 0){
				context.commit('ADD_PERSON',value)
			}else{
				alert('添加的人必須姓王!')
			}
		},
		addPersonServer(context){
			axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(
				response => {
					context.commit('ADD_PERSON',{id:nanoid(),name:response.data})
				},
				error => {
					alert(error.message)
				}
			)
		}
	},
	mutations:{
		ADD_PERSON(state,value){
			console.log('mutations中的ADD_PERSON被調用了')
			state.personList.unshift(value)
		}
	},
	state:{
		personList:[
			{id:'001',name:'張三'}
		]
	},
	getters:{
		firstPersonName(state){
			return state.personList[0].name
		}
	},
}
//count.js 求和相關的配置
export default {
	namespaced:true,		//命名空間需設置為true
	actions:{
		jiaOdd(context,value){
			console.log('actions中的jiaOdd被調用了')
			if(context.state.sum % 2){
				context.commit('JIA',value)
			}

		},
		jiaWait(context,value){
			console.log('actions中的jiaWait被調用了')
			setTimeout(()=>{
				context.commit('JIA',value)
			},500)
		}
	},
	mutations:{
		JIA(state,value){
			console.log('mutations中的JIA被調用了')
			state.sum += value
		},
		JIAN(state,value){
			console.log('mutations中的JIAN被調用了')
			state.sum -= value
		},
	},
	state:{
		sum:0, //當前的和
		school:'尚硅谷',
		subject:'前端',
	},
	getters:{
		bigSum(state){
			return state.sum*10
		}
	},
}

//該文件用於創建Vuex中最為核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
import countOptions from './count'
import personOptions from './person'
//應用Vuex插件
Vue.use(Vuex)

//創建並暴露store
export default new Vuex.Store({
	modules:{
		countAbout:countOptions,
		personAbout:personOptions
	}
})

組件使用

<!--person.vue-->
<template>
	<div>
		<h1>人員列表</h1>
		<h3 style="color:red">Count組件求和為:{{sum}}</h3>
		<h3>列表中第一個人的名字是:{{firstPersonName}}</h3>
		<input type="text" placeholder="請輸入名字" v-model="name">
		<button @click="add">添加</button>
		<button @click="addWang">添加一個姓王的人</button>
		<button @click="addPersonServer">添加一個人,名字隨機</button>
		<ul>
			<li v-for="p in personList" :key="p.id">{{p.name}}</li>
		</ul>
	</div>
</template>

<script>
	import {nanoid} from 'nanoid'
	export default {
		name:'Person',
		data() {
			return {
				name:''
			}
		},
		computed:{
			personList(){
				return this.$store.state.personAbout.personList
			},
			sum(){
				return this.$store.state.countAbout.sum
			},
			firstPersonName(){
                //官方規定獲取指定模塊的getters
				return this.$store.getters['personAbout/firstPersonName']
			}
		},
		methods: {
			add(){
				const personObj = {id:nanoid(),name:this.name}
				this.$store.commit('personAbout/ADD_PERSON',personObj)
				this.name = ''
			},
			addWang(){
				const personObj = {id:nanoid(),name:this.name}
				this.$store.dispatch('personAbout/addPersonWang',personObj)
				this.name = ''
			},
			addPersonServer(){
				this.$store.dispatch('personAbout/addPersonServer')
			}
		},
	}
</script>

<template>
	<div>
		<h1>當前求和為:{{sum}}</h1>
		<h3>當前求和放大10倍為:{{bigSum}}</h3>
		<h3>我在{{school}},學習{{subject}}</h3>
		<h3 style="color:red">Person組件的總人數是:{{personList.length}}</h3>
		<select v-model.number="n">
			<option value="1">1</option>
			<option value="2">2</option>
			<option value="3">3</option>
		</select>
		<button @click="increment(n)">+</button>
		<button @click="decrement(n)">-</button>
		<button @click="incrementOdd(n)">當前求和為奇數再加</button>
		<button @click="incrementWait(n)">等一等再加</button>
	</div>
</template>

<script>
	import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
	export default {
		name:'Count',
		data() {
			return {
				n:1, //用戶選擇的數字
			}
		},
		computed:{
			//借助mapState生成計算屬性,從state中讀取數據。(數組寫法)
			...mapState('countAbout',['sum','school','subject']),
			...mapState('personAbout',['personList']),
			//借助mapGetters生成計算屬性,從getters中讀取數據。(數組寫法)
			...mapGetters('countAbout',['bigSum'])
		},
		methods: {
			//借助mapMutations生成對應的方法,方法中會調用commit去聯系mutations(對象寫法)
			...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
			//借助mapActions生成對應的方法,方法中會調用dispatch去聯系actions(對象寫法)
			...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
		},
		mounted() {
			console.log(this.$store)
		},
	}
</script>

<style lang="css">
	button{
		margin-left: 5px;
	}
</style>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM