Vuex快速入门


学习一个新的知识点,我们先问自己两个问题: 它是什么,它能解决什么问题。弄懂这两点以后,我们再去学习如何用它。首先介绍一些vuex的基本概念:

概念

官方的解释是这样的: Vuex是Vue应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

看着有些头疼,到底什么是状态管理模式,什么是集中式存储管理呢?

通俗的解释:

状态管理模式:状态可以大概理解为变量,那么就是对变量进行管理。变量管理主要包括三部分:state(数据+状态)、view(视图)、actions(行为),三者之间形成一个‘单项数据流’环,如下图:State映射到View上,View监听用户输入,触发Actions,Actions对State进行改变

在这里插入图片描述

集中式存储管理:将变量全部放在一起进行管理,那什么时候需要用到集中式管理呢?

看下面张图,如果组件1想访问组件2或3中的状态(数据),组件2想访问组件1、3中的状态,如果他们之间存在关系,那么可以通过props和 p a r e n t 、 parent、 parentchildren等拿到对应的状态。

在这里插入图片描述

但是现在他们是相互独立,毫无关系的,所以我们就需要将公有访问的数据抽取出来,存储在一个共有的模块中,以一个全局单例模式进行管理,所有的组件都可以按照某种特定方式进行访问或者修改,当在某个组件中修改状态后,其他组件中该值也对应发生变化,这就是集中式存储管理

在这里插入图片描述

好了,那么 Vuex是什么呢?

是一种对状态集中式管理的模式,方便组件之间实现状态共享

那么Vuex解决了什么问题呢?

解决多个视图依赖同一个状态,并且在不同视图上进行状态变更后,其他视图也需要同时变更到同一个状态

Vuex的安装使用

npm install vuex --save (vuex在运行依赖和开发依赖中都需要,所以就直接–save)

使用:

新建文件夹(store)—> 创建index.js ->引入vuex

import Vuex from 'vuex'
import Vue from 'vue'

Vue.use(Vuex) //安装
//创建对象
const store = new Vuex.Store({
    
})
export default store //store默认导出

在main.js中引入:

import Vue from 'vue'
import App from './App.vue'
import store from './store/index' //导入

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store, //挂载
}).$mount('#app')

Vuex的核心

Vuex的核心主要下面5个部分:

​ state

​ mutations

​ actions

​ getters

​ modules

state

state(状态),即定义数据的地方,我们把所有公有数据定义在这里。

定义

const store = new Vuex.Store({
  state: {
    counter: 10,
  }
})

在组件中获取vuex状态

直接获取

//template中
<h2>{{$store.state.counter}}</h2> 
//script中
this.$store.state.counter 

使用mapState辅助函数+computed计算属性:当一个组件需要获取多个状态时

import {mapState} from 'vuex' 
export default {
  computed: {
    // 使用对象展开运算符将此对象混入到外部对象中
    ...mapState({
      myCounte: function(state) {
        return state.counter
      },
      myCounte1: state => state.counter, //es6的缩写形式
    })
  },
}

注意:Vuex采用的是单一状态树模式(SSOT,simple source of truth),这个意味着在vuex中有且仅有一个实例,所有的数据都集中一个实例中,方便我们访问。下面解释一下什么是有且仅有一个实例:像下图这样,就是多个state实例

在这里插入图片描述

mutations

定义同步方法的地方,也是唯一修改state值的地方,但是这个 ’唯一‘ 是相对于 devTools而言的


在介绍mutations的用法之前,我们一起来了解一下Devtools(google商店的一款插件,用来调试vue)的一个作用位置:

在这里插入图片描述

上面图中,可以看到 Devtools只在Mutatios作用,为了能让Devtools能够监听到数据的变化,我们应尽可能只在Mutations方法中改变state。

如果需要发送异步请求,利用返回的数据去改变state中状态的值,则需将函数定义到Actions中,然后在回调函数中通过Commit调用Mutations的方法,Devtools才能相应的监听到。

**注意:**同步和异步定义函数无论定义在哪里,我们的view都会正常的显示。但是为了方便使用Devtools调试,所以我们遵守这种规则,当然,你也有权力说我不用Devtools进行调试,那么也可以不遵守这种规则,理论上来说数据是正确的。

理解完Devtools的作用机理后,我们就应该明白 同步函数应该定义在 mutations中,而异步函数需要定义在actions中。


mutaions的用法

每个mutations都有一个字符串的事件类型(type)和一个回调函数(handler),我们在回调函数中进行状态的更改。

定义

const store = new Vuex.Store({
  state: {
    counter: 1
  },
  mutations: {
    increment (state,payload) {//第一个参数默认state,第二个参数是接收的传递过来的参数,对于第二个参数,我们把他称之为mutations的载荷
      state.counter += payload
    }
  }
})

mutation的第一个参数默认state,第二个参数是传递过来的参数,对于第二个参数,我们把他称之为mutations的载荷

使用:

载荷可以直接传递一个值

this.$store.commit('increment',20) 

载荷也可以是一个对象:

//方式一
this.$store.commit('increment',{ //传递对象
	counter: 20
}) 
//方式二,我们可以将 ’increament‘也包含到对象中传递过去,也能识别到,但是需要type标签
this.$store.commit({
    type: 'increment',
	counter: 20
}) 

当然,传递过去的是对象,那么在 payload中接收到的也是对象,所以如果我们要访问counter的话,需要用 payload.counter

 mutations: {
    increment (state,payload) {
      state.counter += payload.counter
    }
  }

actions

上面我们了解到了,actions和mutaions类似,但是actions是定义异步方法的地方,actions要改变状态的话,需要用context.commit 提交一个mutation。

用法:

先来一个简单的用法,因为我没有接口去调用,所以就用setTimeout模仿异步请求接口的效果吧

定义:

  actions: {
    incrementAction (context,payload) {
      setTimeout(()=>{ //setTimeout异步加载
        console.log(payload.message);
        context.commit('increment',payload.conuter) //commit到mutations
      },1000)
    }
  }

和mutations相似,actions也有两个参数:第一个是默认参数context,我们把他上下文,它代表的是$store,第二个参数就是我们传递过去的负荷

使用:

和mutations一样也可以直接传递一个参数,也可以传递一个对象

//直接传参
this.$store.dispatch('incrementAction',20)
//传递对象
this.$store.dispatch('incrementAction',{
    counter:20
})
//将 ’increament‘也包含到对象中传递过去
this.$store.dispatch({
    type: 'incrementAction'
    counter:20
})

简单的会了,我们再来一个稍微复杂的:

actions通常是异步的,那么我们如何能知道action什么时候结束呢?两种方法:

1 再调用actions的地方,通过返回对象执行回调(这里用词不是很精准,你们就看代码吧,代码中详细注释,自己能调用一下最好)

定义:

 actions: {
	incrementAction2(context,payload) {
      setTimeout(()=>{
      	 console.log(payload()); //回调执行调用过来的函数
      },1000)
    },
    incrementAction3(context,payload) {
      setTimeout(()=>{
        const sussMsg = '执行成功返回数据!';//模拟请求接口成功后返回的数据
        payload.success(sussMsg) //将数据传递给success函数
        context.commit('addPropertyLf',payload.counter)
      },1000)
    }
  }

调用:

methods: {
   changeCounter2() {//调用actions,通过返回函数执行回调
      this.$store.dispatch('incrementAction2',() => '我已经执行成功了')
    },
    changeCounter3() {//调用actions,通过返回带函数的对象执行回调
      this.$store.dispatch('incrementAction3',{
        counter: 30,
        success: (data) => { //将函数赋给success 
          console.log(data); //打印异步请求成功后的返回数据
          return "执行成功了"
        }
      })
    }
 }

2 使用promise,再定义actions时返回一个primise,然后再 store.dispath处接收action的返回值

定义:

actions: { 
  incrementAction4(context,payload){
      return new Promise((resolve,reject) => { //通过promist来管理异步加载,然后将promise作为返回值
        setTimeout(()=>{
          console.log('INCREMENTACTION3在这里执行');
          resolve('我是回调返回值')
        },1000)
      }) 
    },
}

使用:

methods: {
  changeCounter4(){//采用优雅的promise来管理异步加载
      this.$store.dispatch('incrementAction4','采用promise').then(data =>{
        console.log(data)
      })
    }
}

前面的this.$store.dispatch(‘incrementAction4’,‘我是采用promise的’)执行后返回的一个 promise对象,所以接着调用then(data => {}),来对异步请求返回的数据进行逻辑处理

getters

getters和组件中的computed原理相同,如果要对状态执行一些复杂的操作,就可以在getter中执行

用法

getter默认两个参数,第一个是state,第二个是getters,我们可以通过第一个参数获取状态,通过第二个参数或者getters中的其他方法,可以互相进行调用,比如下面的例子

定义:

const store = new Vuex.Store({
 state: {
     students: [{name: 'lf', age: 18, sports: 'football'},
               {name: 'ly', age: 19, sports: 'basketball'},
               {name: 'lz', age: 21, sports: 'ping-pang ball'},
               {name: 'lq', age: 22, sports: 'all ball'}],
 },
 getters: {
    //获取20岁以上的学生,返回一个数组
    more20AgeList(state) { //计算属性默认的第一个参数是 state,第二个参数是 getters
      return state.students.filter( stu => stu.age > 20)
    },
    //获取20岁以上的学生的数量
    more20AgeLength(state,getters) { 
      return getters.more20AgeList.length //通过getters调用more20AgeList方法,计算length
    },
  }
}

上面的getter实现对students数组中年龄大于20的学生进行过滤,在获取20岁以上学生的数量的时候,因为我们已经在前面有求20岁以上学生的信息的方法,所以可以直接调用getters.more20AgeList获取到学生信息的数组,然后再后面加上length就可以直接算出人数了。

好的,基本方法已经会了,但是如果现在有一种需求,需要自己动态传参进getter,然后利用动态的参数进行逻辑操作应该怎么办呢?

利用闭包进行:

当我们再定义闭包的时候,返回一个带参数的函数,我们在调用的时候,虽然getters不能传参,但是我们 可以为喊闭包函数传参,上代码:根据我们动态传的年龄对数据进行筛选

定义:

const store = new Vuex.Store({
 state: {
     students: [{name: 'lf', age: 18, sports: 'football'},
               {name: 'ly', age: 19, sports: 'basketball'},
               {name: 'lz', age: 21, sports: 'ping-pang ball'},
               {name: 'lq', age: 22, sports: 'all ball'}],
  },
  getters: {
 	moreAgeList(state,getters){
 		return function(age) { //返回一个带参函数
 			return state.students.filter(function(stu){ //进行过滤
 				return stu.age > age
 			})
 		}
 	},
 	//上面那个是很全的代码,但是看起来未免复杂,采用es6的语法简写
 	moreAgeList2(state,getters){
 		return age => state.students.filter( stu => stu.age > age)
    }
  }
}

采用es6语句,就看起来超级简单整洁 了

调用:

  <h2>{{$store.getters.more20Age3(21)}}</h2>

采用$store.getters.more20Age3获取返回的带参函数,即这一块:

在这里插入图片描述

然后将21传递给function的age,并且最终return过滤后的数组。

最后,在多啰嗦一句叭,大家都知道计算属性其实室友getter和setter的吧,是可以给计算属性传进参数的,但是由于setter用的比较少,所以就省略了,然后吧getter简写了,所以我们如果想给计算属性传参,但是不想写完整的getter和setter,也可以用这种 闭包函数的形式。

modules

如果全部把所有的状态处理方法全部都写在一起,文件未免显得臃肿,所以vuex给我们提供了modules运行把store分割独立的模块,每个模块都拥有自己的state、mutatios、actions、getters、modules(不过这个嵌套子模块,不建议层级嵌套太多)。

用法:

如果把一个人一生的所有东西全部都包含在一个store对象中,就会有很多,因此我们对他进行模块分割,比如把他的爱好、工作等单独分出来作为子模块

定义:

const hobby = {
  state: { 
    book: ["JS高级编程","vue从入门到精通"]
  },
  mutations: {
    addBook(state,payload){
      Vue.set(state.book,2,payload)
    }
  },
  actions: { },
  getters: { 
    //倒叙输出所有的book
    reverseBook(state,getters){
      return state.book.slice().reverse()
    }
  }
}
const work = {
  state: { },
  mutations: { },
  actions: { },
  getters: { }
}

const store = new Vuex.Store({
  state: {
    name: "lf",
    age: 18
  },
  modules: {
    work,
    hobby
  }
})

子模块中state的获取、getters的调用、mutations的调用

    <h2>{{$store.state.hobby.book}}</h2>
    <h2>{{$store.getters.reverseBook}}</h2>
    <button @click="addJava">点我增加book</button>
    
    <script>
	  export default {
	   methods: {
         addJava() {
           this.$store.commit('addBook','java虚拟机')
         },
        }
	  }

注意:这里罗列一下devTools下面的数据结构:

在这里插入图片描述

关于子模板的对象:

​ 以对象的形式封装进state的,所以我们在调用的时候要用 $store.state.moduleName.perpertyName的形式

关于getters和mutation:都是直接封装进去的,所以大家在子模板中给getter还有mutation取名的时候,记得不要重名,如果不小心重名,会优先在主store 中查找该方法,找到就返回,如果没有找到,就去子store 中找,如果有多个子store 的方法重名,谁先初始化出来谁的优先级就高。

记录自己学习的每一点,大家如果发现有错误的地方,请及时指正我,谢谢!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值