Vuex
Vuex
Vuex
是适用于在Vue
项目开发时使用的状态管理工具。试想一下,如果在一个项目开发中频繁的使用组件传参的方式来同步data
中的值,一旦项目变得很庞大,管理和维护这些值将是相当棘手的工作。为此,Vue
为这些被多个组件频繁使用的值提供了一个统一管理的工具——Vuex
。在具有Vuex
的Vue项目中,我们只需要把这些值定义在Vuex
中,即可在整个Vue
项目的组件中使用。
Vuex
的安装由于学Vuex
的时候,已经学习了vue
自动化工具Vue-Cli
,所以下面的目录结构请参照Vue-Cli2.x
脚手架生成的文件目录。
但是前提是你已经完成了一个基础的Vue
项目的起步构建,并且已经pwd
到该项目的根目录下。
Vuex
npm install vuex -s 或者 yarn add vux -s
store
文件夹,在该文件夹内创建index.js
Vue
项目文件:组件文件、路由文件、入口文件、静态资源文件等等。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
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)
})
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
中的属性。( 那你要问可以改吗,可以但是不好,后面会说明为什么不建议 )Vue
专用开发调试工具VueDevtools
(时间旅行)在Vue
项目开发中,需要监测项目中的各种值,为了提高效率,Vue
提供了一款浏览器开发者专用扩展程序——VueDevtools
.
没有安装此扩展程序的小伙伴可根据下面gitHub
地址下载使用,本文不再赘述。
https://github.com/vuejs/vue-devtools
Vuex
中的核心知识在Vuex对象中,其实不仅仅有上文提到的state,还有用来操作(增删改)state中状态导的方法集合等等。
成员列表:
state
--> 用来存放公共状态mutations
--> state
成员操作一方法集合getters
--> 加工state
成员给外界actions
--> 涉及相关的异步操作modules
--> Vuex
中相关的模块化状态管理
上图看的有点懵?我来给你详细解释。
首先,Vue
组件 即 上图所示 Vue Components
如果调用某个 VueX
的方法过程中需要向后端请求时或者说出现异步操作时,需要先用 dispatch
来调用VueX
中 action
方法集中的同名方法,以保证数据的同步。可以说,actions
方法集的存在就是为了让 mutations
方法集中的同名方法能在异步操作中起作用。如同上图绿色通道。
如果说没有异步操作,name
我们就可以直接在组件内提交状态的 mutations
方法集中编写 dispatch
的同名方法来直接达成对state
成员的操作。注意:1.3.3节
中有提到,不建议在组建中直接对state
中的公共状态进行操作,这是因为直接在组件中修改(例如:this.$store.state.name="hello react"
)的话就不能被刚才安装的 VueDevtools
监控到数据的变化。如同上图红色通道。
最后经过 mutations
方法集中的同名方法修改后的 state
成员会被渲染到组件的原位置当中去。
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)
})
}
Mutations
方法集mutations
是操作 state
数据的方法的集合,比如对该数据的修改、增加、删除等等。
mutations
方法集的使用方法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
中:
Mutations
方法传递参数在实际生产过程中,会遇到需要在提交某个 mutations
的时候需要携带一些参数给方法使用。
单个值提交时:
this.$store.commit("MutationsEdit","tom")
当需要提交多个参数的时候,推荐将他们整合到一个数组中来进行参数传递。
this.$store.commit("MutationsEdit",{ name : "tom" } )
另外一种提交的方式:
this.$store.commit({
type: "MutationsEdit",
payload:{
name:"tom"
}
})
Mutations
方法增删state中的成员状态为配合 Vue
的响应式数据,我们在用 Mutations
中的方法时,应当用 Vue
规定的方法来进行操作。如果用别的形式去增删,则 Vue
不能对该数据进行监听,不能做到响应式。
Vue.set
为某个对象中添加一个响应式数据ageVue.set(state,"age",15)
Vue.delete
删除成员Vue.delete(state,"age")
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
Module
: VueX
的模块化开发当项目庞大的时候,状态非常多,管理在一个 store
中显得有些臃肿,Vuex
给我们提供了模块化管理模式。 Vuex
允许我们将store
分割成模块(module
)。每个模块都拥有自己独立的 基本属性 Stete、Mutations、Actions、getters
、甚至是嵌套子模块——从上到下进行同样方式分割。
然后在创建store
的js
文件引入这些模块,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
}
})
getter
getter
的话,模块内部的getter
会有三个参数,第一个是模块内部定义的state
状态,第二个是模块内部定义的getters
,第三个是根节点状态值state
(rootState
)第四个是根节点的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 ) {
..., // 异步代码
}
由于store
对象中的moduels
被模块化了,所以在调用的时候,要在前面加一个模块名,避免各个模块中的状态名或者函数集合中的函数重名,只有命名空间配置之后才加。详细看 2.5.4 节
// 在原来获取的基础上加上模块名
this.$store.state.moduleC.cName;
辅助函数获取也相同,什么是辅助函数 ? 详 2.5.5 节
之前的 getter, mutations,actions,state
他们默认都是在全局命名空间的,所以我们默认是可以和使用根状态一样去使用他们,但是这样不可避免会出现命名冲突的问题,所以是模块有更高的封装性和复用性,我们可以通过添加 namespaced:true
使其成为带命名空间的模块。当模块被注册后,它的所有的 getter action mutation 以及 state
都会根据模块注册的路径去调整命名。
// moduleC 模块导出的时候加一个 namespaced:true
export default {
namespaced:true,
state,
getters,
actions,
mutations
}
VueX
辅助函数VueX
辅助函数有哪些?辅助函数:mapState
、mapGetters
、mapActions
、mapMutations
VueX
辅助函数有什么作用?官方的解释是:
当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState
辅助函数帮助我们生成计算属性,让你少按几次键
当初在看到这个解释的时候可能觉得非常空洞,难以理解。生成计算属性是什么?少按几次键???
mapState
是state
的语法糖,什么是语法糖?我对语法糖的理解就是,我明明已经对一种操作很熟练了,并且这种操作也不存在什么问题,为什么要用所谓的"更好,更好的操作"?,用了一段时间后,真香!
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,
})