Vuex是做什么的?
- 官方解释: Vuex是一个专为Vue.js应用程序开发的状态管理模式
- 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
好多新名词!!!听不懂~~ 不要着急 慢慢来
比如说我们这里有三个组件,若组件一中有种状态组件二想使用 组件三也想使用,那我们应该把这个状态存放到哪里呢?
那么你就可以将状态管理先看成把需要多个组件共享的变量全部存储在一个对象里面。
要是用之前所学的方法我们可以这样实现:
const shareObj = {
name: 'Venciki'
}
Vue.prototype.shareObj = Obj;
原理是:所有的组件都继承自Vue的原型。这样,所有组件都可以共享shareObj了。但是我们这种办法做不到响应式。
这个时候我们就是用到了Vuex插件,所以说Vuex就是为了提供这样一个在多个组件间共享状态的插件。
但是,有什么状态需要我们在多个组件间共享呢?
- 比如用户的登录状态、用户名称、头像、地理位置信息、商品的收藏、购物车种的物品等等。
- 这些状态都可以放在同一的地方,对它进行保存和管理,而且他们还是响应式的 马上就可以看到代码了qaq
直接上手把:
- 安装
npm install vuex --save
- 配置
在src路径下创建store文件夹,再store文件夹下创建index.js。基本配置:
import Vue from 'vue'
import Vuex from 'vuex'
//1.安装插件
Vue.use(Vuex)
//2.创建对象
const store = new Vuex.Store({
state: {
counter: 1000
},
mutations: {
},
actions: {
},
getters: {
},
modules: {
}
})
//3.导出
export default store
而这里的store对象里面的state就是我们所说的状态.
因为store对象的创建和vue-router的原理相似都是在vue的原型上创建对象Vue.prototype.$store = store
所以当我们要在别的组件中使用 这个状态 时,只需调用$store.state.counter
这个时候要是修改counter的值,不建议直接用$store.state.counter
,而是在index.js中的mutations中添加方法:
mutations: {
//方法
increment(state) {
state.counter ++
},
decrement(state) {
state.counter--
}
},
在组件中使用时需要写触发函数:
methods: {
addition() {
this.$store.commit('increment') //
},
subtraction() {
this.$store.commit('decrement')
}
}
注意这里不是直接调用this.$store.increment()
方法,而是通过this.$store.commit('decrement')
提交。再把这两个函数绑定给要点击的按钮,就完成了counter的加减。
以上就是Vuex的一个基本用法
Vuex核心概念
用来存放状态信息的
单一状态树:
在一个项目中建立一个store
-
Getters
有时候,我们需要从store中获取一些state变异后的状态,比如我们现在想要全局状态counter的平方
- 我们可以这样写<h2>{{$store.state.counter * $store.state.counter}}</h2>
, 但是这种写法很复杂会让代码整体显示的很乱。这个时候我们就可以接用getters:
- 可以直接在getter中调用counter,再通过函数实现平方, 如下:getters: { powerCounter(state) { return state.counter * state.counter } },
这样我么就可以直接在各个组件中这样调用:
<h2>{{$store.getters.powerCounter}}</h2>
我门再来看下面这种情况:
我们在store中的state中定义了个名为student的数组,每一项都是对象存放学生的姓名的年龄:state:{ student: [ {name: 'kobe', age: 20}, {name: 'curry', age: 18}, {name: 'james', age: 19}, {name: 'yao', age: 24}, ] }
此时我们想要在某个组件中获取student中所有年龄大于等于20的学生数据,如果不是用getters,我们可以这样做:
//在某个组件中设置计算属性computed computed:{ more20stu() { return this.$store.state.student.filter(s => { return s.age >= 20 }) } },
然后组件标签中这样调用
<h2>{{more20stu}}</h2>
,结果显示成功了:
这样虽然能显示想要的结果,但是如果我们要在好多组件中都使用的话,必须得再每个组件中的computed中设置这个计算属性,这样就显得很麻烦。我们可以直接在getters中设置全局的计算属性:
getters:{
more20stu(state) {
return state.student.filter(s => s.age >= 20);
}
}
这样我们在每个组件中都可以通过{{$store.getters.more20stu}}来获取相应的结果。
getters还可以内部套用
比如说我们在获取年龄大于20的需求上,再加一个需求:返回年龄大于等于20学生的个数
我们来直接来看代码:
getters: {
powerCounter(state) {
return state.counter * state.counter
},
more20stu(state) {
return state.student.filter(s => s.age >= 20);
},
more20stuLength(state, getters) { //还可以传第二个参数
return getters.more20stu.length;
}
},
因为我们在getters中已经设置了 返回年龄大于等于20的学生的对象数据,想要获取个数直接.length就可以了。getters还可以内部套用想必大家看代码直接就秒懂了吧!!
有同学这个时候就问了,getters中的计算属性可以从外部动态传参吗? 答案是可以的。这是如果我们在优化一下上面的需求,我们不让返回年龄大于20的学生了,让在使用这个计算属性的时候把这个限制年龄动态的传进来:
moreAgeStu(state) {
return function(age) {
return state.student.filter(s => s.age >= age);
}
}
这样我们在使用的时候就可以把限制年龄传进来了,<h2>{{$store.getters.moreAgeStu(19)}}</h2>
-
Mutation
前面我们有了解到mutation 现在我们对它做一些补充
Vuex的store状态的更新唯一方式: 提交 Mutation
mutation主要包括两部分:- 字符串的事件类型
- 一个回调函数,该回调函数的第一个参数就是state
现在我们又来改需求了,(没错我就是那个烦人的产品经理)之前用mutations只是单纯的实现 ++ 和 --,我们现在需要让他能加我们传入的值,能减我们传入的值,也就是说调用add的时候传入一个5,那么就是counter原来的基础上+5,反之亦然~~
这个东西难点在于,不知道从哪儿给mutation传值:
//在组件中的methods中
addCounter(num) {
this.$store.commit('incrementCounter', num)
}
//在store/index.js中
mutations: {
//方法
incrementCounter(state, num) {
state.counter += num
}
},
就是这样传参的:在commit提交的时候附加着参数的传递
参数被称为是mutation的再荷(Payload)
如果要传多个数据的话commit中直接可以改为对象
this.$store.commit({
type: 'incrementCounter',
num
})
但是这个时候再mutations中接受时就要注意了,第二个参数直接是传入的是commit中的对象。这里我们直接叫第二个参数为payload
incrementCounter(state, payload) {
state.counter += payload.num
}
mutation响应规则
-
Vuex的store中的state是响应式的,当state中的数据发生改变时,Vue组件会自动更新。
-
这就要求我们必须遵守一些Vuex对应的规则
-
提前再store中初始化好所需的属性。
-
当给state中的对象添加新属性时 ,使用下面的方式:
- 方式一:使用Vue.set(obj, ‘newProp’, 123)
- 方式二:用新对象给旧对象重新赋值
删除可以用Vue.delet()
-
mutation类型常量
我们可以把mutation中的类型常量抽离出来放在 新建的一个mutations-types.js文件中,再通过import引入
例如:mutations-types.js
const INCREMENT = 'increment'
我们在组件中引入:
import{
INCREMENT
} from '....这里放js文件的路径'
那在使用的时候也有一点变化
组件中
addition() {
this.$store.commit(INCREMENT)
}
//commit中原来的字符串改为变量名
//而在mutations中也要做出改变:
[INCREMENT](state) {
state.counter ++
},
//将原来的类型名改为[]+变量名
Mutation同步函数
通常情况下,Vuex要求我们Mutation中的方法必须时同步方法
如果是异步方法,devtools不能追踪到这个操作
-
Action
有些情况下,我们希望在Vuex中进行一些异步操作,比如网络请求,必然是异步的,这个时候我们就会使用到Action来代替Mutation进行异步操作的
比如说我们要异步修改state中info的内容
actions: {
//context上下文
aUpdateInfo(context){
setTimeout(() => {
context.commit('updateinfo')
}, 1000)
}
},
而updateinfo此时是一个已经封装好的同步执行函数
而这个时候组件methods中也不能使用commit了,要使用dispatch:
updateinfo(){
// this.$store.commit('updateinfo');
this.$store.dispatch('aUpdateInfo')
}
这个时候devtools就能追踪到这个异步操作
而action中函数传递参数的方式和mutations中传递参数的方式是一致的
组件中传入
updateinfo(){
// this.$store.commit('updateinfo');
this.$store.dispatch('aUpdateInfo', '我是payload')
}
action中
actions: {
//context上下文
aUpdateInfo(context, payload){
setTimeout(() => {
context.commit('updateinfo')
console.log(payload) //我是‘payload’
}, 1000)
}
},
action还有一个特别神奇的地方:
actions: {
//context上下文
aUpdateInfo(context, payload){
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('updateinfo')
console.log(payload);
resolve('111')
}, 1000)
})
}
},
我们在aUpdateInfo里面直接返回一个Promise 然后在我们的异步操作中执行resolve
接来下就是见证奇迹的时刻
updateinfo(){
// this.$store.commit('updateinfo');
this.$store
.dispatch('aUpdateInfo', '我是payload')
.then(res => {
console.log('里面完成了提交');
console.log(res)
})
}
是的你没有看错我们可以直接在组件的函数中this.$store .dispatch('aUpdateInfo', '我是payload')
后面直接跟上.then(),res就是resolve传出的参数,这样我们就完成了一个操作:就是让我们修改完后,让外面知道我们修改完了,外面可以得到成功信息后再执行下一个操作。
前面我们说过Vue使用单一状态数,那么意味着很多状态都会交给Vuex来管理
当应用变得非常复杂时,store对象就有可能变得相当臃肿
为了解决这个问题,Vuex语序我们将store分割成模块(module),每个模块拥有自己的state、mutations、action、getters等
也就是说我们可以再module额外定义state、mutations、action、getters等内容
我们假设现在有一个moduleA,那我们先要在store的modules:{ a: moduleA},做这样的设置。然后再moduleA中的state中设置一个name,
const moduleA ={
state: {
name: 'venciki'
},
}
在组件中的使用方法:
{{$store.state.a.name}}
其他重点和之前使用方法类似
项目结构
为了不让store/index.js那么臃肿,我们可以把index中的mutations、action、getters抽离到单独的js文件中。再通过导出,导入到indexjs中。这样就会使项目解构特别清晰。