Vuex是什么?为什么要用它?
Vuex 是一个专门为 Vue.js 应用程序开发的状态管理模式 ,它采用集中式存储管理应用的所有组件状态,并以相应的规则保证状态以一种可预测的方式发生变化。可以理解为:将多个组件共享的变量全部存储在一个对象里面,然后将这个对象放在顶层的 Vue 实例中,让其他组件可以使用,它最大的特点是响应式。
官方解释
- Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
大白话
状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。
其实,可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。
然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。
那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?
如果是这样的话,为什么官方还要专门出一个插件Vuex呢?难道我们不能自己封装一个对象来管理吗?
当然可以,只是我们要先想想VueJS带给我们最大的便利是什么呢?没错,就是响应式。
如果你自己封装实现一个对象能不能保证它里面所有的属性做到响应式呢?当然也可以,只是自己封装可能稍微麻烦一些。
一般情况下,我们会在 Vuex 中存放一些需要在多个界面中进行共享的信息。比如用户的登录状态、用户名称、头像、地理位置信息、商品的收藏、购物车中的物品等,这些状态信息,我们可以放在统一的地方,对它进行保存和管理。
Vuex 插件的安装
js
npm install --save vuex@3.6.2
注意版本问题:vue 的 2.x 版本对应 vuex 的 3.x 版本,vue 的 3.x 版本对应 vuex 的 4.x 版本
在 src 目录下新建 store 文件夹,创建 index.js文件引入、安装、创建并导出Vuex对象。
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
和 vue-router 的使用方式一样,在 main.js 文件中挂载使用
js
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
store 对象中存放的东西是固定的,主要有:state、mutations、actions、getters、modules
下图是官方给出的vuex状态管理图例
Vuex的基本使用
安装浏览器插件:devtools 方便调试
state:存放需要共享的状态信息,使用时通过 $store.state.counter 即可拿到状态信息。
js
const store = new Vuex.store({
state:{
counter:1000
},
mutations:{//定义一些方法
increment(state){
state.counter++
},
decrement(state){
state.counter--
}
}
对 state 的状态信息进行修改:先拿到 store 对象,然后通过 commit 提交 mutations 中的方法。
js
methods:{
sub(){
this.$store.commit('decrement')
},
add(){
this.$store.commit('increment')
}
}
什么时候用vuex
****当你无法很好进行数据管理的时候,多个组件需要共享数据 时,你更需要用Vuex,即:1.多个组件依赖于同一状态 2.来自不同组件的行为需要变更为同一状态
如果组件过多且需要数据共享,那么全局事件总线实现数据共享就太过麻烦,此时如果使用vuex便可以更好的进行数据管理。
使用总结:Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此------如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
Vuex 核心概念
State:
单一状态树即单一数据源,在一个项目中只使用一个store对象,来存储所有共享的状态信息。
Getters:
类似于计算属性,在数据展示前进行一些变化处理,具有缓存功能,能够提高运行效率。
js
getters:{
powerCounter(state){
return state.counter * state.counter
},
more20stu(state){
return state.students.filter(s => s.age > 20)
},
more20stuLength(state,getters){
return getters.more20stu.length
},
moreAgeStu(state){
return function(age){
return state.students.filter(s => s.age > age)
}
}
}
使用时,通过:$store.getters.powerCounter 获取:
js
<h2>{{$store.getters.powerCounter}}</h2>
<h2>{{$store.getters.more20stu}}</h2>
<h2>{{$store.getters.more20stuLength}}</h2>
<h2>{{$store.getters.moreAgeStu(18)}}</h2>
需要手动传参数时,可以在 getters 中返回一个 function:
js
<h2>{{$store.getters.moreAgeStu(18)}}</h2>
State:
概念 State是提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储。
如果状态信息是保存到多个Store对象中的,那么之后的管理和维护等都会变得特别困难,所以Vuex也使用了单一状态树(单一数据源Single Source of Truth)来管理应用层级的全部状态。
单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。
js
export default new Vuex.Store({
state: {
count: 0
},
}
Mutations:
Mutation
用于变更存储在Store
中的数据。
- 只能通过
mutation
变更Store
数据,不可以直接操作Store
中的数据 - 通过这种方式,虽然操作稍微繁琐一些,但可以集中监控所有数据的变化,而直接操作
Store
数据是无法进行监控的
Mutation响应规则
Vuex的store中的State是响应式的,当State中的数据发生改变时,Vue组件也会自动更新。
这就要求我们必须遵守一些Vuex对应的规则:
提前在store中初始化好所需的属性 当给State中的对象添加新属性时,使用如下方式: 使用Vue.set(obj,'newProp','propValue') 用新对象给旧对象重新赋值
store/index.js
js
mutations:{//定义一些方法
increment(state){
state.counter++
},
decrement(state){
state.counter--
},
incrementCount(state, payload){
//1.普通提交方式
//state.counter += count
//2.特殊提交方式
state.counter += payload.count
},
addStudent(state, obj){
state.students.push(obj)
}
}
组件调用 :传递的参数(payload)可以是一个对象
js
<template>
<div>
<button @click="addCount(5)">+5</button>
<button @click="addCount(10)">+10</button>
<button @click="addStudent({id:105, name:'name6', age:29})">添加学生</button>
</div>
</template>
<script>
export default {
name:"HelloVuex",
methods:{
addCount(count){
//1.普通的提交风格
// this.$store.commit('incrementCount',count)
//2.特殊的提交风格
this.$store.commit({
type:'incrementCount',
count:count
})
},
addStudent(stu){
this.$store.commit('addStudent',stu)
}
}
}
</script>
<style>
</style>
mutations在处理异步操作时,能够引起页面的响应式变化,但是 devtools 无法进行监听。
比如:在 mutations 中执行以下代码
js
updateInfo(state){
setTimeout(() => {
state.info.name = 'James'
}, 1000);
}
Actions:
如果确实需要进行一些异步操作,比如网络请求,建议在 Actions 中进行处理,这样 devtools 就能够进行跟踪,由 Actions 处理异步操作,具体的函数部分仍交由 Mutations 进行处理。
js
actions:{
//context:上下文 === store
aUpdateInfo(context,payload){
setTimeout(() => {
context.commit('updateInfo',payload)
console.log(payload);
}, 5000);
}
}
组件中使用时,调用:this.$store.dispatch('aUpdateInfo')
js
updateInfo(){
// this.$store.commit('updateInfo')
this.$store.dispatch('aUpdateInfo','参数')
}
Modules:
Module是模块的意思,为什么会在Vuex中使用模块呢?
Vues使用单一状态树,意味着很多状态都会交给Vuex来管理 当应用变的非常复杂时,Store对象就可能变的相当臃肿 为解决这个问题,Vuex允许我们将store分割成模块(Module),并且每个模块拥有自己的State、Mutation、Actions、Getters等
分模块管理数据
js
const moduleA = {
state:{
name: 'moduleA'
},
mutations:{
updateName(state,payload){
state.name = payload
}
},
getters:{
fullname(state){
return state.name + '1111'
},
fullname2(state, getters){
return getters.fullname + '2222'
},
fullname3(state, getters, rootState){
//传入第三个参数:rootState为上一个store对象中的state
return getters.fullname2 +rootState.counter
}
},
actions:{
aUpdateName(context){//context 中 的commit只指向该模块中的mutations
setTimeout(() => {
context.commit('updateName','xiaowang')
console.log(context)
},
const store = new Vuex.Store({
state:{
counter:1000,
students:[
{id:110, name: 'name1', age: 12},
{id:111, name: 'name2', age: 21},
{id:112, name: 'name3', age: 22},
{id:113, name: 'name4', age: 20},
{id:114, name: 'name5', age: 18}
],
info:{
name: 'kobe',
age: 40,
height: 1.89
}
},
mutations:{//定义一些方法
increment(state){
state.counter++
},
decrement(state){
state.counter--
},
incrementCount(state, payload){
//1.普通提交方式
//state.counter += count
//2.特殊提交方式
state.counter += payload.count
},
addStudent(state, obj){
state.students.push(obj)
},
updateInfo(state){
state.info.name = 'Jams'//响应式:事先定义过的为响应式
// state.info['address'] = 'chengdu'//响应式
// Vue.set(state.info,'address1','Asgin')//响应式
// delete state.info.age//响应式
// Vue.delete(state.info,'height')//响应式
}
},
getters:{
powerCounter(state){
return state.counter * state.counter
},
more20stu(state){
return state.students.filter(s => s.age > 20)
},
more20stuLength(state,getters){
return getters.more20stu.length
},
moreAgeStu(state){
return function(age){
return state.students.filter(s => s.age > age)
}
}
},
actions:{
//context:上下文 === store
aUpdateInfo(context, payload){
return new Promise((resolve, reject)=>{
setTimeout(() => {
context.commit('updateInfo');
console.log(payload);
resolve('11111')
}, 1000);
})
}
},
modules:{
a: moduleA
}
})
组件中使用 :$store.state.a
js
<h2>Modules中的内容</h2>
<h2>{{$store.state.a.name}}</h2>
<button @click="updateName">修改模块ModuleA中的名字</button>
<h2>{{$store.getters.fullname3}}</h2>
<button @click="aupdateName">actions修改name</button>
执行模块中的方法 :直接 $store.commit 提交,故 mutations 之间定义的方法名不能重复。
js
updateName(){
this.$store.commit('updateName','lisa')
},
aupdateName(){
this.$store.dispatch('aUpdateName')
}