【 Vue 】看了这篇VueX,才算真正掌握 Vuex !

VueX(Vue状态管理模式)

一、初识Vuex

1.1 关于 Vuex

Vuex 是适用于在Vue项目开发时使用的状态管理工具。试想一下,如果在一个项目开发中频繁的使用组件传参的方式来同步data中的值,一旦项目变得很庞大,管理和维护这些值将是相当棘手的工作。为此,Vue为这些被多个组件频繁使用的值提供了一个统一管理的工具——Vuex。在具有Vuex的Vue项目中,我们只需要把这些值定义在Vuex中,即可在整个Vue项目的组件中使用。

1.2 Vuex 的安装

由于学Vuex的时候,已经学习了vue自动化工具Vue-Cli,所以下面的目录结构请参照Vue-Cli2.x脚手架生成的文件目录。
但是前提是你已经完成了一个基础的Vue项目的起步构建,并且已经pwd到该项目的根目录下。

  • 使用Npm安装或者yarn安装 Vuex
npm install vuex -s   或者 yarn add vux -s
  • 在项目的根目录(路由平级目录)下新增一个store文件夹,在该文件夹内创建index.js
    此时项目中的目录结构应该是下图这样的。
    已经有基本的Vue项目文件:组件文件、路由文件、入口文件、静态资源文件等等。

【 Vue 】看了这篇VueX,才算真正掌握 Vuex !_第1张图片

1.3 使用

1.3.1 初始化store文件下的index.js文件

import Vue from "vue" // 引入Vue
import Vuex from "vuex" //引入vuex

//使用插件 (use)
Vue.use(Vuex)
//创建一个变量存储创建的store实例
const store = new Vuex.Store({
//Vuex中几个基本属性下面会详细讲解
state:{
// 存放的键值对就是将来要进行公共管理的状态
	name:"helloVuex"
})
//讲定义好的 store 变量进行导出
export default store

1.32 讲导出的store导入main.js入口文件并挂载到当前项目创建的Vue实例上面

main.js 代码初始化,具体就不细说了,就是导入导出。

import Vue from "vue"
import App from "./App"
import router from "./store"

Vue,config.productionTip = false 

new Vue({
	el:"#app",
	router,
	store,
	render: h => h(app)
})

1.33 Vuex 在组件中的应用

例如在App.vue(俗称根组件)中,我们要将store中的state对象中定义的name属性拿过来用在h1标签中显示

<template>
// template 标签中有且仅能有唯一一个节点。
	<div id="app">
		name:
		<h1>{{ $store.state.name }}</h1>
	</div>
</template>

或者要在组件中的某一方法调用该属性

...,//省略
methods:{
	fnuc(){
		console.log(this.$store.state.name)
		}
}, 
...,
注意,不建议在此处更改state中的属性。( 那你要问可以改吗,可以但是不好,后面会说明为什么不建议 )

1.4 安装Vue专用开发调试工具VueDevtools(时间旅行)

Vue项目开发中,需要监测项目中的各种值,为了提高效率,Vue提供了一款浏览器开发者专用扩展程序——VueDevtools.
没有安装此扩展程序的小伙伴可根据下面gitHub地址下载使用,本文不再赘述。

https://github.com/vuejs/vue-devtools

【 Vue 】看了这篇VueX,才算真正掌握 Vuex !_第2张图片
在学习Vuex的时候,这个扩展程序显得更加重要。

二、 Vuex中的核心知识

在Vuex对象中,其实不仅仅有上文提到的state,还有用来操作(增删改)state中状态导的方法集合等等。

成员列表:

  • state --> 用来存放公共状态
  • mutations --> state成员操作一方法集合
  • getters --> 加工state成员给外界
  • actions --> 涉及相关的异步操作
  • modules --> Vuex中相关的模块化状态管理

2.1 VueX的工作流程

【 Vue 】看了这篇VueX,才算真正掌握 Vuex !_第3张图片
上图看的有点懵?我来给你详细解释。
首先,Vue组件 即 上图所示 Vue Components 如果调用某个 VueX 的方法过程中需要向后端请求时或者说出现异步操作时,需要先用 dispatch 来调用VueXaction 方法集中的同名方法,以保证数据的同步。可以说,actions方法集的存在就是为了让 mutations 方法集中的同名方法能在异步操作中起作用。如同上图绿色通道。
如果说没有异步操作,name 我们就可以直接在组件内提交状态的 mutations 方法集中编写 dispatch 的同名方法来直接达成对state成员的操作。注意:1.3.3节中有提到,不建议在组建中直接对state中的公共状态进行操作,这是因为直接在组件中修改(例如:this.$store.state.name="hello react")的话就不能被刚才安装的 VueDevtools 监控到数据的变化。如同上图红色通道。
最后经过 mutations 方法集中的同名方法修改后的 state 成员会被渲染到组件的原位置当中去。

2.2 Actions方法集

由于是离组件操作最近的方法集,所以先介绍。因为直接在 mutations 方法集中对数据进行异步操作,将会引起数据失效。所以Vuex提供了一种专门用来进行异步操作的方法集,最终生成一个同名方法向 mutations 方法集中传递参数。
Actions 中的自定义 mutations 同名函数中有两个默认参数。

  • context 上下文(相当于箭头函数中的this对象)
  • payload 挂载的参数
    例如,我们在两秒钟后执行 mutations 方法集的同名函数 Edit 方法 。Actions 中名为什么,mutations 当中就名为什么。相呼应,即为同名函数。
    由于要演示异步操作,所以应用一个延时函数:setTimeout
actions:{
	ActionsEdit(context,payload){
		setTimeout(() => {
			context.commit( "MutationsEdit",payload)
		},2000)	
	}
}


//上面的写法更加被人们简写为
ActionsEdit({commit},payload){
		//直接在第一个对象中将commit函数结构出来,这样写用的多一点
		setTimeout(() => {
			commit( "MutationsEdit",payload)  //这里调用的函数名即为  mutations方法集中命名的同名函数
		},2000)	
	}
}

触发Actions中的函数,需要在组件中调用一个Actions方法集中的同名函数,参考 Actions 调用 Mutations 同名函数。

var payload = { age :18 }
this.$store.dispatch("ActionsEdit", payload)

由于Actions 是执行一个异步操作,提到异步最先想到是什么呢?没错就是 Promise ,尝试封装一个 Promise 对象

Actions({commit},payload){
	return new Promise((resolve,reject) => {
		setTimeout(() => {
			commit("MutationsEdit",payload)
			resolve()
		},2000)
	})
}

2.3 Mutations 方法集

mutations 是操作 state 数据的方法的集合,比如对该数据的修改、增加、删除等等。

2.3.1mutations 方法集的使用方法

mutations 方法集中的方法都有默认的形参:

(【state】【,payload】)
  • state 是当前 Vuex 对象中的 state ,存储的公共状态
  • payload 是该方法在被调用时用来传递参数。
    例如,我们编写一个方法,当被执行时,能把下面的 name 值修改为 "jacktom" ,由于不涉及异步操作,所以直接执行 mutations 的同名函数即可。

index.js

import Vue from "vue"
import Vuex from "Vuex"

Vue.use(Vuex)
const store = new Vuex.store({
	state:{
		name:"hello Vuex"
		},
	mutations{
		MutationsEdit(state,payload){
			state.name = "jack"+payload
			}
		}
})

export default store

而在组件中直接调用 commit 方法同名函数。例如在App.vue 中的某一个 methods 中:

2.3.2 Mutations 方法传递参数

在实际生产过程中,会遇到需要在提交某个 mutations 的时候需要携带一些参数给方法使用。
单个值提交时:

this.$store.commit("MutationsEdit","tom")

当需要提交多个参数的时候,推荐将他们整合到一个数组中来进行参数传递。

this.$store.commit("MutationsEdit",{ name : "tom" } )

另外一种提交的方式:

this.$store.commit({
	type: "MutationsEdit",
	payload:{
		name:"tom"
	}
})

2.3.3 Mutations 方法增删state中的成员状态

为配合 Vue 的响应式数据,我们在用 Mutations 中的方法时,应当用 Vue 规定的方法来进行操作。如果用别的形式去增删,则 Vue 不能对该数据进行监听,不能做到响应式。

  • Vue.set 为某个对象中添加一个响应式数据age
Vue.set(state,"age",15)
  • Vue.delete 删除成员
    将刚才添加的成员删除
Vue.delete(state,"age")

2.4 Getters

可以对 state 中的成员进行加工之后再向外传递。
Getters中有两个默认的参数

  • state 当前VueX对象中的状态成员对象
  • getters 当前getters对象,用于将getters下的其他getter拿来用

例如

getters:{
	nameInfo(state){
		return "姓名:"+state.name
		},
		fullInfo(state,getters){
			return getters.nameInfo+"年龄"+state.age
			}
}

在某组件中调用

this,$store.getters.fullInfo

2.5 ModuleVueX的模块化开发

当项目庞大的时候,状态非常多,管理在一个 store 中显得有些臃肿,Vuex 给我们提供了模块化管理模式。 Vuex 允许我们将store 分割成模块(module)。每个模块都拥有自己独立的 基本属性 Stete、Mutations、Actions、getters 、甚至是嵌套子模块——从上到下进行同样方式分割。

2.5.1 模块化开发的文件结构

【 Vue 】看了这篇VueX,才算真正掌握 Vuex !_第4张图片
然后在创建storejs文件引入这些模块,store.js 如下

import moduleA from "./modules/moduleA、index.js"
import moduleB from "./modules/moduleB.js"

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

2.5.2 模块内部的各个属性函数的参数发生了变化

  • getter

getter的话,模块内部的getter会有三个参数,第一个是模块内部定义的state状态,第二个是模块内部定义的getters,第三个是根节点状态值staterootState)第四个是根节点的getters

// moduleC
const getters = {
	cFullName:( state , getters , rootState ) => ` full${state,cName} `
}
  • mutations

mutations 里面的回调函数传入的第一个参数也是 模块内部定义的 state ,另一个是 挂载的参数 payload。就不贴代码了。

  • actions

最后的actions的话,传入的还是一个上下文对象 context,然后 多了一个 根节点的状态值,附代码。

// moduleC
const actions= {
	ActionsEdit({ state , commit ,rootState },payload ) {
		..., // 异步代码
}

2.5.3 模块内部的状态获取

由于store对象中的moduels被模块化了,所以在调用的时候,要在前面加一个模块名,避免各个模块中的状态名或者函数集合中的函数重名,只有命名空间配置之后才加。详细看 2.5.4 节

// 在原来获取的基础上加上模块名
this.$store.state.moduleC.cName;

辅助函数获取也相同,什么是辅助函数 ? 详 2.5.5 节

2.5.4 模块命名空间

之前的 getter, mutations,actions,state 他们默认都是在全局命名空间的,所以我们默认是可以和使用根状态一样去使用他们,但是这样不可避免会出现命名冲突的问题,所以是模块有更高的封装性和复用性,我们可以通过添加 namespaced:true 使其成为带命名空间的模块。当模块被注册后,它的所有的 getter action mutation 以及 state都会根据模块注册的路径去调整命名。

// moduleC 模块导出的时候加一个 namespaced:true
export default {
	namespaced:true,
	state,
	getters,
	actions,
	mutations
}

2.5.5 VueX 辅助函数

2.5.5.1 VueX辅助函数有哪些?

辅助函数:mapStatemapGettersmapActionsmapMutations

2.5.5.2 VueX 辅助函数有什么作用?

官方的解释是:

当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键

当初在看到这个解释的时候可能觉得非常空洞,难以理解。生成计算属性是什么?少按几次键???

mapStatestate的语法糖,什么是语法糖?我对语法糖的理解就是,我明明已经对一种操作很熟练了,并且这种操作也不存在什么问题,为什么要用所谓的"更好,更好的操作"?,用了一段时间后,真香!

2.5.5.3 VueX 辅助函数怎么用?

如果在一个组件中需要获取多个状态,通过this.$store.state来声明就会显得有些重复跟冗余,通过mapState这个辅助函数可以帮助生成计算属性。
store.js

state:{
	number1:1,
	number2:2
}
mutations:{
    changeNumber1(state, val){
      state.number1 += val
    },
    changeNumber2(state, val){
      state.number2 += val
    },
  },

在组件中使用辅助函数要先将函数结构出来。

// App.vue
<template>
  <div>
    <h1>{{number1}}</h1>
    <h1>{{number2}}</h1>
  </div>
</template>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default{
	computed:{
		...mapState(['number1','number2']) // 在组件通过this就可调用,不用再写this.$store.state
		// ...mapState({ 另一种写法
    	//   number1: state=>state.number1,
    	//   number2: state=>state.number2
    	// }),
	},
	 methods:{
    ...mapMutations(['changeNumber1','changeNumber2']),
    // 定义别名方式
    // ...mapMutations({
    //   changeNumber1Alias: 'changeNumber1',
    //   changeNumber2Alias: 'changeNumber2'
    
    //上面再调用的话直接用自己取的这个名字即可。
    // }),
  }
// 同理运用 getters 和 actions 
}

当辅助函数遇到模块化,怎么获取模块内的状态呢?
代码如下,一种是在前面加模块的命名空间。

...,
...mapGetters({
	bGetters:"moduleB/fullName" // 当然这里也可以重命名
})

这样写每次都要写模块名,这样写下来很烦,所以这些辅助函数给我们提供了一个参数的位置让我们来绑定模块的命名空间。

...,
// moduleB 模块内的状态Bname
...mapState( " moduleB " ,{
	name: state => state.bName
})

//  同理mapActions 和 mapMutations 也可以这样改写。
...mapAction( " moduleB " , [
	"/ASYNC_SET_NAME"
])

除此之外,VueX 还给我们提供了一个函数,如果你当前的组件用的都是同一个子模块的话,我们可以用 createNamespacedHelpers 来创建基于某一个子模块命名空间的辅助函数,就不需要在写子模块的名字或者占用参数位置。如下

import { createNamespacedHelpers } from " Vuex "
const { mapState , mapActions } = createNamespacedHelpers( "moduleB" ) // 当前组件所用子模块名称

这样创建之后,就可以用之前的写法来访问到模块的状态了。

...,
...mapState({
	bName : state => state.bName,
})

你可能感兴趣的:(Web前端,Vue,vue,vue.js)