Vuex的使用以及案例
我们知道组件之间是独立的,组件之间想要实现通信,我目前知道的就只有props选项,但这也仅限于父组件和子组件之间的通信。如果兄弟组件之间想要实现通信呢?当我们做中大型项目时,面对一大堆组件之间的通信,还有一大堆的逻辑代码,会不会很抓狂?那为何不把组件之间共享的数据给提取出来,在一定的规则下管理这些数据呢? 这就是Vuex的基本思想了。
13.1 Vuex简介
什么是Vuex?官方说法:Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。简单来说vuex是vue的一个插件,用来实现组件之间数据通讯的。
想要了解Vuex的功能,重点是理解什么是状态管理,我们这里使用一个计数器的小应用进行知识点的引入。现有一个页面点击的计数器,伪代码如下。
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
这个小应用中有三个重要的部分:
• state,驱动应用的数据源;
• view,以声明方式将 state 映射到视图;
• actions,响应在 view 上的用户输入导致的状态变化。
三个部分组成了一个简单的计数器,他们之间的关系我们可以叫做“单向数据流”。如图13.1所示。
图13.1 单向数据流
state作为我们的数据,展示在view视图中,视图中的针对用户的操作进行相应,调用执行了函数,也就是actions,接下来actions的函数又对我们的数据也就是state进行了改变,在整个流程中三个部分之间的目前来说是一一对应的,也就是说state只显示在一个view视图上,也只被一个action的函数所改变,所以称之为单向数据流。那么如果我们的需求发生了变化,我们不仅仅要在某个页面进行计数,我们要求这个计数的总数是全局共享的,也就是在所有的页面中都可以改变state的值。这个时候就出现了一个问题:当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏。体现在多个视图依赖于同一状态,以及来自不同视图的行为需要变更同一状态。
因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为,这种模式就是状态管理。vuex就是vue中实现状态管理的插件。
13.2 Vuex入门
理解什么是Vuex之后我们直接来上手实际演示一下Vuex如何使用。Vuex的使用场景大多是大型的单页面应用,因为此时很可能需要使用到组件外部管理状态。废话不多说,我们开始学习Vuex的使用。
13.2.1 搭建Vue应用
首先使用vue-cli来构建一个vue的开发环境,项目取名为vuex_demo。这里主要使用的知识点是vue-cli,使用vue create vuex_demo命令完成创建。之前章节已经有过对脚手架的学习,这里不再详细介绍。
13.2.2 安装vuex
vuex有多种安装方式,我们这里介绍其中两种,分别是直接下载 /CDN 引用,以及nmp命令。实际项目中建议使用命令nmp命令的方式。
• 直接下载 / CDN 引用
在 Vue 之后引入 vuex 会进行自动安装:
<script src="/path/to/vue.js"></script>
<script src="/path/to/vuex.js"></script>
• NPM
npm install vuex --save
进入vuex_demo应用下执行npm install vuex --save命令后,打开package.json文件,在dependencies中找到vuex的依赖就表明安装成功,如图13.2所示。
图13.2 package.json
13.2.3 编写代码
我们当前应用完成一个简单的计数器,页面中包含当前点击次数,默认为0,以及一个按钮,每点击一次安装计数器的值加一。
我们在src目录下新建store文件夹,store表示存储的意思,我们使用vuex来存储一些共享的信息。并且在store文件夹中vuex.js文件,此文件用于vuex插件的实例化。Vuex.js文件代码如例13-1所示。
例13-1 vuex.js
1 //引入模块
2 import Vue from 'vue'
3 import Vuex from 'vuex'
4 //在一个模块化的打包系统中,您必须显式地通过 `Vue.use()` 来安装 Vuex:
5 Vue.use(Vuex)
6
7 //#实例化Vuex.store ,并进行相关配置
8 export default new Vuex.Store({
9 //存储状态
10 state: {
11 count: 0
12 },
13 //其中定义函数,用于变更store中的状态
14 mutations: {
15 increment (state) {
16 console.log('执行了递增')
17 state.count++
18 }
19 }
20 })
接下来我们还需要修改main.js文件,在vue实例中添加store选项。main.js文件关键代码如例13-2所示。
例13-2 main.js
1 import Vue from 'vue'
2 import App from './App.vue'
3 //引入vuex.js模块
4 import Store from './store/vuex'
5
6 Vue.config.productionTip = false
7
8 new Vue({
9 //配置store选项
10 store:Store,
11 render: h => h(App),
12 }).$mount('#app')
最后我们修改App.vue文件,在其中操作如何展示state的值,以及触发器递增,App.vue代码如例12-2所示。
例12-3 App.vue
1 <template>
2 <div id="app">
3 count:
4 <!-- $store获取到vue实例存储选项中保存在state中的count的值进行显示 -->
5 <h1>{{ $store.state.count }}</h1>
6 <!--点击按钮触发increment函数-->
7 <button @click="increment">点击</button>
8 </div>
9 </template>
10
11 <script>
12
13
14 export default {
15 name: 'App',
16 methods:{
17 increment:function(){
18 //调用当前vue应用实例的store选项,并且执行函数名为increment的函数
19 this.$store.commit('increment')
20 }
21 }
22
23 }
24 </script>
25
26 <style>
27 #app {
28 font-family: Avenir, Helvetica, Arial, sans-serif;
29 -webkit-font-smoothing: antialiased;
30 -moz-osx-font-smoothing: grayscale;
31 text-align: center;
32 color: #2c3e50;
33 margin-top: 60px;
34 }
35 </style>
36
37
13.2.3 运行项目
运行项目之后可以看到页面中显示了定义在store中state里定义的count的值,默认为0,运行效果如图13.3所示。
图13.3 运行结果1
在点击按钮之后count的值随着点击次数递增并且显示在页面中,点击3次按钮后效果如果13.4所示。
图13.4 运行结果2
13.2.4 代码解释
以上代码中关键的步骤:
• 使用vuex模块,初始化Vuex.Store实例,配置state,mutations选项。
• 配置main.js,使全局的vue实例配置store选项,添加对vuex的使用。
• 页面中应用state的值,已经触发mutations函数改变state的值。
当然当前的配置并不完整,针对Vuex.store实例化的完整配置如下。
#实例化Vuex.store ,并进行相关配置
export default new Vuex.Store({
state: {
//存储状态
},
mutations: {
//变更store中的状态
},
actions: {
//类似于mutation,
//action提交的是mutation,而不是直接变更状态
//action可以包含异步操作
},
getters:{
//state的派生状态
},
modules: {
//将store分割成模块
}
})
我们发现,在VueX对象中,其实不止有state,还有用来操作state中数据的方法集,以及当我们需要对state中的数据需要加工的方法集等等成员,成员列表:
• state 存放状态
• mutations state成员操作
• getters 加工state成员给外界
• actions 异步操作
• modules 模块化状态管理
成员之间相互配合的流程如图13.5所示。
图13.5 vuex流程
首先,Vue组件如果调用某个VueX的方法过程中需要向后端请求时或者说出现异步操作时,需要调用VueX中actions的方法,如果没有异步操作,那么我们就可以直接在组件内提交状态中的Mutations中自己编写的方法来达成对state成员的操作,最后被修改后的state成员会被渲染到组件的位置当中去。
13.3 state访问状态对象
state用于存储共享状态,我们可以在其中保存共享数据,他是全局的,在任意组件中都可以通过this.$store.state.属性名获取对应的状态数值。
#存储共享状态
state:{
num:100,
userinfo:{}
}
#组件中
#通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到
#由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态
computed:{
num(){
return this.$store.state.num;
}
},
13.4 mutations修改状态
mutations是操作state数据的方法的集合,比如对该数据的修改、增加、删除等等。
13.4.1 mutations使用方法
mutations中的方法都有默认的形参:
([state] [,payload])
• state是当前VueX对象中的state
• payload是该方法在被调用时传递参数使用的
比如我们刚才写的案例中,只使用了state一个参数:
//#实例化Vuex.store ,并进行相关配置
export default new Vuex.Store({
//存储状态
state: {
count: 0
},
//其中定义函数,用于变更store中的状态
mutations: {
increment (state) {
console.log('执行了递增')
state.count++
}
}
})
组件中如果想要改变state中的某个值,不能获取值之后直接赋值,需要调用mutations中的函数的方式进行对state中值改变的操作。commit中的字符串参数就是mutations中定义的函数的函数名。
this.$store.commit('increment')
13.4.2 mutations传递参数
如果我们每次count的值不是递增,而是指定数值大小的递增,此时就需要使用额外的参数,此时可以使用payload参数。
vuex.Store初始化部分的代码如下。
mutations: {
//第二个参数用于传值
increment (state,payload) {
// 变更状态
//state.num++
state.num+=payload
}
}
组件中调用代码如下。
methods:{
increment(){
//第一个参数指定执行mutations中的哪一个函数,第二个参数是执行函数时候传递的参数
this.$store.commit('increment',45);
}
}
注意:一条重要的原则就是要记住 mutation 必须是同步函数。
13.5 getters计算过滤操作
有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数。Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getters中的方法有两个默认参数:
• state 当前VueX对象中的状态对象。
• getters 当前getters对象,用于将getters下的其他getter拿来用。
比如我们要对保存在state中的student数组数据进行过滤,这返回30岁以下的数据。此时就可以使用getters接受state作为第一个参数,对state中的数据进行过滤,二次处理。
//vuex实例化
getters:{
//过滤年龄小于30
filter_age(state){
return state.student.filter(item => item.age<30);
}
}
//组件中获取过滤后的数据
//Getters 会暴露为 store.getters 对象,你可以以属性的形式访问这些值
this.$store.getters.filter_age
13.6 actions异步修改状态
actions和之前讲的Mutations功能基本一样,不同点是,actions是异步的改变state状态,而Mutations是同步改变状态。
在实例化路由时对actions进行设置语法如下,在actions的函数中调用mutations里的函数。我们会发现这actions和mutations的中的方法传递的参数也不一样,这里是context,表示上下文对象,这里你可以理解称store本身。
actions: {
increment (context) {
context.commit('increment')
}
}
在组件中调用actions中函数的方式也与mutation调用不同,actions函数执行通过store.dispatch()触发,调用方式如下。
//Action 通过 store.dispatch 方法触发:
store.dispatch('increment')
13.7 module模块组
随着项目的复杂性增加,我们共享的状态越来越多,这时候我们就需要把我们状态的各种操作进行一个分组,分组后再进行按组编写。module:状态管理器的模块组操作。
也就是说由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
#moduleA.js
const moduleA = {
state: () => ({ xx }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
export default moduleA;
#modulesB.js
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
#index.js
import moduleA from './moduleA.js'
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
//使用:this.$store.state.a.xx
13.8 本章小结
• Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。
• 使用命令npm install vuex --save完成vuex的安装
• 代码上vuex中通过实例化Vuex.store ,并进行相关配置来实现状态管理。
• Vuex 实例化时包含state,mutations,actons,getters,module选项。
• state用于存储共享状态,我们可以在其中保存共享数据。在任意组件中都可以通过this.$store.state.属性名
• mutations是操作state数据的方法的集合,比如对该数据的修改、增加、删除等等。
• mutations中的方法都有默认的形参:([state] [,payload]),state是当前VueX对象中的state,payload是该方法在被调用时传递参数使用的。
• Vuex 允许我们在 store 中定义“getter”属性来实现对数据的过滤。
• actions和之前讲的Mutations功能基本一样,不同点是,actions是异步的改变state状态,而Mutations是同步改变状态。
3.9 理论习题与实践练
1.思考题
1.1 请简述你是怎么理解状态管理的。
1.2 请思考状态管理的使用场景。
1.3 请简述vuex的使用步骤。
1.4 请简述Vue.Store对象实例化所需要的选项都有哪些?
1.5 请简述如何获取读取state中某个数据的值。
1.6 请简述通过mutations实现对state中数据的值的改变。
2.编程题
• 2.1. 使用vuex实现购物车数据的保存功能
- 点赞
- 收藏
- 关注作者
评论(0)