Vue.js是一个渐进式的框架,是一个分层的设计模式。一共分为五层,核心库为基础,在这基础上添加组件系统、客户端路由、大规模状态管理和开发环境。
其中大数据状态管理对应的技术就是vuex,它是由Vue框架开发团队提供的一个插件。
仓库vuex的作用:
能够在vuex中集中管理共享的数据,易于开发和后期维护;
能够高效地实现组件之间的数据共享, 提高开发效率;
存储在 vuex中的数据都是响应式的,能够实时保持数据与页面的同步。
学习使用这个功能之前,我们先看一下它的结构:
可以看到vuex在图中由Actions、Mutaitions、State组成,与组件实例化构成一个循环。
其中State为状态其实就是存放的数据;actions直译为行为,就是一个会对数据修改的一个异步行为,可以理解为是一个中转站;Mutaitions中是对数据真正的操作。
引入vuex
第一种方式:通过CDN引入
第二种方式:npm i vuex -save下载包再进行配置
在main.js中配置:
//导入:
import Vuex from "vuex"
Vue.use(Vuex)
//创建仓库
const store=new Vuex.Store({
//配置state、actions、mutaitions等
})
//挂载到vm对象
new Vue({
render(h){return h(app)},
router,
store//挂载以后 所有的组件就可以直接从store中获取全局数据
}).$mount( "#app")
第三种方式:vue cli脚手架自己配置,在vue create "项目名"时,选择上vuex,脚手架会自动帮我们创建成功。
具体来学习vuex的配置项
1、state状态
就是我们共享数据的存放位置,就与data书写形式一样,如:
var store=new Vuex.Store({
state: {
msg:"仓库中的数据",
arr:[{id:001,name:"zs"},{id:002,name:"ls"}],
obj:{a:1}
},
在组件中获取数据时,如获取msg为例,通过this.$store.state.msg。
这里的this.$store,是在引入时通过Vue.use(Vuex)时给Vue对象原型上绑定了的对象,所有组件中都可以使用。
2、getter
它就好比我们学过的计算属性,每个方法内都会传入state,可以操作state内的数据。
例如:返回state中两个数的和
var store=new Vuex.Store({
state: {
a:1,
b:2
},
getters:{
sum(state){
return state.a+state.b
}
}
})
获取sum:this.$store.getters.sum
3、Mutaitions
这个配置用于修改state中的数据,官方文档中它是在同步代码中实现,但是我们自己测试的时候在里面写异步代码也能实现。
还有,官方文档中说它是更改 Vuex 的 store 中的状态(数据)的唯一方法,不要用赋值表达式直接在组件中给store设置新数据,但是直接赋值效果也能够实现。
组件中修改state的数据,需要用到$store一个方法commit(),它的作用是传递参数并触发mutaition里对应的方法(专业说法为提交负荷)。
commit()的传参
1、普通形式:commit("name",d),这里的name为mutaitions中配置的方法名,d为我们传递的参数,可以是数字、数组、对象等。
2、以对象形式传入:commit({type:"name",d:dd}),把整个对象作为了第二个参数传入mutations,type对应的就是mutaitions中配置的方法名。
mutaitions里面的配置:
mutations: {
name(state,arg){
//业务
},
name为方法名,第一个参数是传入的state,第二个参数就是commit传来的参数。
完整实现一下:在box组件中修改state中的值
box.vue:
mounted(){
this.$store.commit("change",11)
}
store下的index.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)//让组件的原型链中 有仓库的工具代码
export default new Vuex.Store({
state: {
num:1
},
mutations: {
change(state,arg){
state.num=arg
}
},
})
程序一运行,state中的num由1就变成了11.
4、Action
Action 可以包含任意异步操作,它的作用就像一个中转站,当修改的state的代码不是异步的或者是没有什么逻辑判断,我们直接在组件中commit操作Mutaitions;但是当存在异步操作或者很多逻辑判断时,我们一般就要加上Action这一步,组件中先将数据传递给Action,Action处理后再传给Mutaitions。
组件中传递信息给Action需要用到$store上的dispacth方法,它的传参跟commit传参形式是一样的,一种是普通形式,一种是对象形式。
actions中的配置是方法传来的两个参数,第一个不再是state,而是一个上下文对象,是一个简化了的store对象;第二个参数为组件中传递的数据。
流程非常简单,就是:组件——>Action——>Mutaition
代码展示:
组件中:
mounted(){
this.$store.dispatch("change",11)
}
actions和mutaitions中:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)//让组件的原型链中 有仓库的工具代码
export default new Vuex.Store({
state: {
num:1
},
actions:{
change(context,arg1){
//异步处理
setTimeout(()=>{
context.commit("change1",arg1)
})
}
},
mutations: {
change1(state,arg2){
state.num=arg2
}
},
})
5、Module
可用于业务分块开发: 由于使用单一状态树,应用的所有状态(数据)会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。 为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter
具体使用:
分块做业务时,一个对应一个store这样不容易混乱,而且模块化开发更容易维护。
一个人对应一个js文件,在里面配置自己的 state、mutation、action、getter,然后导入导出到index.js文件中,放到modules配置项中。
如:自己的js文件
export default {
namespaced: true,//局部命名空间(让state的中变量与其他模块中的同名变量不冲突)
state: {
msg:"张三的公共数据"
},
getters: {
...
},
mutations: {
....
},
actions: {
...
}
}
index.js中引入:
import Vue from 'vue'
import Vuex from 'vuex'
import Model1 from "./Model1.js"
Vue.use(Vuex) //让组件的原型链中 有仓库的工具代码
export default new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
Model1,
}
})
使用时,某个组件要使用Model1中的公共数据:this.$store.state.Model1.msg;
要使用Model1中motaitions配置的方法:this.$store.commit("Model1/change",num);
使用getters:this.$store.getters["Model1/x"];
使用dispatch:this.$store.dispatch("Model1/change",num)
mapState,mapGetters,mapMutation,mapAction的使用
mapState
最后介绍一种编写代码的优化,在组件使用state数据时,我们需要this.$store.state.n才能取到数据,当要取大量数据时,this.$store.state都是重复的操作,因此官方给我们了一个mapState方法来优化,借助它来生成计算属性。
要使用首先在组件中,引入这个mapState,如:import {mapState} from 'vuex'
mapState()方法可以传入数组或者对象。
传入对象时:mapState({a:"aa",b:"bb"}),其中a,b为生成的属性名,"aa"和"bb"就是this.$store.state.aa和this.$store.state.bb。
等同于:
computed:{
a(){
return this.$store.state.aa
}
}
这样就可以看出,框架利用mapState将state中数据帮我们生成了计算属性。这个时候我们要使用this.$store.state.aa时,直接使用a就行了,是不是极大的减少了代码量。
传入数组时,更为简洁:mapState(["aa","bb"]),这样的意思就是让计算属性名与state中的属性名相同。等同于对象写法:mapState({aa:"aa",bb:"bb"})。
注意:属性值加引号
mapState(["aa","bb"])运行后,aa就可以取出this.$store.state.aa。
写出代码:
import {mapState} from "vuex"
export default{
computed:{
...mapState(["aa","bb"]),
...mapState({a:"aa",b:"bb"}),//不想计算属性与state数据同名时用
}
}
注意这里mapState前面要加...,这是拓展运算符。因为mapState的返回值是一个对象,需要把它拆开成每一项,形成键值对的形式,否则会报错。
mapGetters
用法与mapState相同,通过mapGetters将它的数据转换为组件的计算属性。两个方法只是名字不同,用法全都相同,故不在详细说明。
mapMutations
前面两个方法是转换为计算属性,优化简写this.$store.state和this.$store.getters
很明显看到mapMutations,我们应该就已经猜到了它的用法。即,优化简写this.$store.commit()
同样它传入的参数也有两种形式:对象和数组
对象写法:mapMutations({a:"a1",b:"b1"}),
等同于:
methods: {
a(value){
this.$store.commit("a1",value)
},
b(value){
this.$store.commit("b1",value)
},
},
细心的同学可以看到,这里的生成的方法需要传参。这里已经生成了a和b方法,在实际使用的时候我们手动给它传一个参数,
如:当点击按钮时,将10传到mutaitons配置的a1方法中
<template>
<div id="app">
<button @click="a(10)"></button>
</div>
</template>
<script>
import {mapMutations} from "vuex"
export default{
methods: {
...mapState({a:"a1",b:"b1"}),
},
}
数组写法:mapMutations(["a1","b1"]),这个的意思就是生成的方法名与mutaitions配置的方法名相同。
注意:加引号
mapActions
用法与mapMutations相同不再叙述。