【Vuex】vue状态机详解

目录

Vuex

1、Vuex的五个属性

2、Vuex工作原理

3、Vuex的使用

在html文件中使用

辅助函数

模块化modules和命名空间namespaced

在vue-cli脚手架中使用


Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

Vuex是一个状态管理模式,也叫状态机,将组件共享的数据放到状态机中统一管理,组件想拿数据可以从自己的数据模型中拿,也可以从仓库中拿,可以把vuex理解成一个仓库。

假设A组件想要拿B组件里面的数据,那就把A组件和B组件的数据放到Vuex中,A组件想要获取B组件数据从vuex中拿,B组件想要获取A组件的数据也从Vuex拿。

1、Vuex的五个属性

{
	//state 维护的是公共状态(数据),类似Vue中的data
	state: {},
	//getters 处理state中的数据并返回新数据,类似计算属性computed
	getters: {},
	//mutations 突变,修改state的唯一方式,只能做同步操作
	mutations: {},
	//actions 动作,处理异步操作,获取后台响应,提交数据给突变mutations
	actions: {},
	// 状态机可以有多个,modules用来存放各模块的状态机
	modules: {}
}

2、Vuex工作原理

vuex三个主要的属性:state、mutations、actions


 

当我们声明了一个状态机并将其注入Vue实例中,那么Vue实例上就会存在一个$store属性,该属性上有dispatch和commit等方法。 

分析上图:

Vue组件将需要执行的方法通过dispatch方法派发给Actions执行,但是Actions不会去执行该方法,于是它又通过commit方法提交该方法给Mutation来执行,Mutation执行完后,将处理完的数据给State,然后State重新渲染数据到页面。

注意:当Vue组件需要操作的数据不需要通过网络请求得到时,可以跳过actions,直接使用commit方法提交给mutations处理。

3、Vuex的使用

在html文件中使用

1、通过CDN引入Vuex

Vuex是Vue的一个插件,使用时需要安装

<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.6.0/vuex.min.js" integrity="sha512-uN93RUcJ9frHH6dyLknjgalFe7JNkfb3OjW4Qgg5xjaVA3U7l0diZ3hGL2Puk/38sp7xD/SLHdNFit7Kq3RbtQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

2、声明状态机

let store = new Vuex.Store(
	{
		//state 维护的是公共状态,类似Vue中的data
		state: {},
		//getters 处理state中的数据并返回新数据,类似计算属性computed
		getters: {},
		//mutations 突变,修改state的唯一方式,只能做同步操作
		mutations: {},
		//actions 动作,处理异步操作,获取后台响应,提交数据给突变mutations
		actions: {}
	}
)

3、将状态机注入Vue实例

let vm = new Vue({
	// 将状态机注入Vue实例
	store,
	el: "#app",
	data: {}
})

此时可以打印Vue实例中的$store看一下:

console.log(vm.$store)

 因此我们可以在插值语法中使用 $store.state.xxx 和 $store.getters.xxx 访问state中的数据和getters中的方法。

例子:

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
	<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
	<!-- 1、引入Vuex -->
	<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.6.0/vuex.min.js" integrity="sha512-uN93RUcJ9frHH6dyLknjgalFe7JNkfb3OjW4Qgg5xjaVA3U7l0diZ3hGL2Puk/38sp7xD/SLHdNFit7Kq3RbtQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>

<body>
	<div id="app">
		<!-- 使用state中的数据和getter中的方法 -->
		{
   
   {$store.state.msg}}
		{
   
   {$store.getters.MSG}}
		<hr>
		{
   
   {$store.state.sum}}
		<button @click="foo">点击加{
   
   {n}}</button>
	</div>
    <script>
	  let store = new Vuex.Store({
		state: {
			msg: 'hello',
			sum: 0
		},
		// getters用来处理state中的数据
		getters: {
			MSG(state){ //默认有一个参数,它是一个对象,里面存放了state中的数据
				// console.log(state);
				return state.msg.toUpperCase()
			}
		},
		actions: {
			// 默认有两个参数:sto是一个类状态机对象; value是dispatch传递过来的数据
			add(sto, value){
				// console.log(sto, value, 'actions');
				// 提交突变给mutations
				// commit('mutations中的方法名', 需要传递的数据)
				sto.commit('ADD', value)
			}
		},
		mutations: {
			// 最终在mutations中处理数据
			// 默认两个参数:state是一个对象,里面存放了state中的数据; value是commit提交的时候传递过来的数据
			ADD(state, value){
				// console.log(state, value, 'mutations');
				state.sum += value
			}
		},
	})

	let vm = new Vue({
		store,
		el: "#app",
		data: {
			n: 2
		},
		methods:{
			foo(){
				// 派发一个动作给actions
				// dispatch('actions中的方法名', 需要传递的数据)
				this.$store.dispatch('add', this.n)
			}
		}
	})
	// console.log(vm); //打印Vue实例,可以看到Vue实例上有一个$store属性
  </script>
</body>

</html>

辅助函数

Vuex提供的组件辅助函数有mapState、mapGetters、mapActions、mapMutations,它们存在Vuex对象上,使用时可以使用Vuex.mapState的方式调用,也可以将它们从Vuex中解构出来使用:

let { mapState, mapGetters, mapActions, mapMutations } = Vuex

1、mapState方法:用于帮助映射 state 中的数据为计算属性

computed:{
	// msg(){
	// 	return this.$store.state.msg
	// },
	// sum(){
	// 	return this.$store.state.sum
	// }
	//-----------------------------------------------------
	// 借助mapState函数生成上述计算属性
	// mapState返回一个对象,我们需要使用扩展运算符将它们展开
	// 1、对象写法
	...mapState({msg:'msg',sum:'sum'})
	// 2、数组写法
	...mapState(['msg','sum'])
},
// 测试,看一下mapState返回值
created(){
	let a = mapState({msg:'msg',sum:'sum'})
	console.log(a); //返回一个对象,对象中有msg、sum方法
},

使用了mapState方法之后,我们在插值语法直接使用{ {msg}}、{ {sum}}即可,不需要写成:{ {$store.state.msg}}、{ {$store.state.sum}},同理mapGetters方法也一样。

2、mapGetters方法:用于帮助映射 getters 中的数据为计算属性

computed:{
	// MSG(){
	// 	return this.$store.getters.MSG
	// }
	//------------------------------------------------------
	// 借助mapGetters函数生成上述计算属性
	// 1、对象写法
	...mapGetters({MSG:'MSG'})
	// 2、数组写法
	...mapGetters(['MSG'])
},

3、mapActions方法:用于帮助生成与 actions 对话的方法,即包含$store.dispatch(xxx)的函数

methods:{
	// foo(){
	// 	this.$store.dispatch('add', this.n)
	// }
	//--------------------------------------------
	// 借助mapActions函数生成上述foo方法
	// mapActions返回一个对象,我们需要使用扩展运算符将它们展开
	// 1、对象写法
	...mapActions({foo:'add'}),
	// 2、数组写法   (注意:需要对象的属性名和属性值一样才能写成下面的数组形式)
	...mapActions(['foo'])
}

4、mapMutations方法:用于帮助生成与 mutations 对话的方法,即包含$store.commit(xxx)的函数

methods:{
	// foo(){
	// 	this.$store.commit('ADD', this.n)
	// }
	//--------------------------------------------
	// 借助mapMutations函数生成上述foo方法
	// mapMutations返回一个对象,我们需要使用扩展运算符将它们展开
	// 1、对象写法
	...mapMutations({foo:'ADD'}),
	// 2、数组写法   (注意:需要对象的属性名和属性值一样才能写成下面的数组形式)
	...mapMutations(['foo'])
	
}

注意:使用 mapActions 和 mapMutations 时,若需要传递参数,那么参数要在模板绑定事件的时候传参。

<button @click="foo(n)">点击加{
   
   {n}}</button>

模块化modules和命名空间namespaced

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块,每个模块拥有自己的 state、mutation、action、getter,甚至是嵌套子模块。namespaced表示设置命名空间。

Vuex中的modules属性用来存放各个模块的状态机。

let aModule = {
	// 开启命名空间
	namespaced: true,
	state:{},
	getters:{},
	actions:{},
	mutations:{}
}
let bModule = {
	namespaced: true,
	state:{},
	getters:{},
	actions:{},
	mutations:{}
}
let store = new Vuex.Store({
	modules:{
		a:aModule,
		b:bModules
	}
})

(1)组件中读取state中数据 

// 方式一:自己读取
this.$store.state.a.xxx
// 方式二:使用mapState读取  表示后面数组里面的属性是a模块的
...mapState('a',['xxx1','xxx2','xxx3'])

 (2)组件中读取getters中的数据

// 方式一:自己读取
this.$store.getters['a/xxx']
// 方式二:使用mapGetters读取  表示后面数组里面的属性是a模块的
...mapGetters('a',['xxx1','xxx2','xxx3'])

(3)组件中调用dispatch

// 方式一:自己直接dispatch
this.$store.dispatch('a/xxx',需要传递的数据)
// 方式二:使用mapActions读取
...mapActions('a',['xxx1','xxx2','xxx3'])

(4)组件中调用commit

// 方式一:自己直接commit
this.$store.commit('a/xxx',需要传递的数据)
// 方式二:使用mapMutations读取
...mapMutations('a',['xxx1','xxx2','xxx3'])

在vue-cli脚手架中使用

写个小案例:当我们点击登录的时候会获取到一个token值,这个token值其他组件也需要使用,因此我们可以把token值存在vuex中,供所有的组件使用。

1、安装vuex

注意:vuex4版本以上只能在vue3中使用,vue2使用vuex3版本,安装时如果不指定版本,会安装最新版本的vuex

npm i [email protected]

2、在src下新建一个文件夹store,然后在store文件夹下再新建一个文件夹login,存放登录相关的状态机

src/store/index.js 

import Vue from 'vue';
import Vuex from 'vuex';
// 引入登录状态机
import login from '../store/login/login';

Vue.use(Vuex);

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

src/login/login.js

import axios from 'axios';
// 这是我登录状态机配置对象
export default {
	// 开启命名空间
	namespaced: true,
	state: {
		token: localStorage.getItem('token') || ""
	},
	gettes: {},
	mutations: {
		SET_TOKEN(state, payload) {
			state.token = payload;
			// 持久化储存 
			localStorage.setItem('token', payload)
		}
	},
	actions: {
		async login({commit}, payload) {
			// 发送异步请求
			let res = await axios.post('登录接口地址', payload)
			// console.log(res);
			commit('SET_TOKEN', res.data.data.token)
		}
	}
}

3、在src下新建文件夹pages,存放组件

login.vue

<template>
	<div>
		登录页面-----
		<button @click='login(obj)'>登录</button>
	</div>
</template>
<script>
import {mapActions} from 'vuex'
export default {
	data(){
		return {
			obj:{
				username:"xxx",
				password:xxx
			}
		}
	},
	methods:{
		// 分发状态机动作 发送登录请求
		...mapActions('login',['login'])
	}
}
</script>

 user.vue

<template>
	<div>
		用户页面--{
   
   {token}}
	</div>
</template>
<script>
import {mapState} from 'vuex';
export default {
	data(){
		return {}
	},
	computed:{
		...mapState('login',['token'])
	}
}
</script>

 

猜你喜欢

转载自blog.csdn.net/lq313131/article/details/127131901