Vue全家桶之Vuex

认识Vuex

Vuex是一个专为Vue应用程序开发的状态管理模式。(就是为了提供一个在多个组件间共享状态的插件)

  • 采用集中式存储管理应用的所有组件的状态

  • 以相应的规则保证状态以一种可预测的方式发生变化。

  • 集成到Vue的官方调试工具 devtools extension中,提供了如:零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

  • 满足响应式

状态管理、集中式存储管理可以理解为把需要多个组件共享的变量全部存储在一个对象里面,再把该对象放在顶层的Vue实例中,其他组件就可以共享这个对象中的所有变量属性。

Vuex与vue.prototype的区别

vuex满足响应式,而vue.prototype不满足响应式

状态管理

一般需要管理的状态:多个状态,在多个界面之间共享

  • 用户的登录状态(token令牌,用户登录成功,服务器返回token,需保存)、用户名称、头像、地理位置等
  • 商品的收藏、购物车的物品等
  • ……

单页面状态管理

在这里插入图片描述

  • view:视图,触发action操作
  • action:操作,可以改变state状态
  • state:状态,state状态变化可以改变view视图内容(state在view视图中展示)

多界面状态管理

  • 某些状态(状态1/状态2/状态3)只属于某一个视图,但也有一些状态(状态a/状态b/状态c)属于多个视图共同维护的
  • 多个视图都依赖同一个状态时,该状态改变,则多个界面需要进行更新;
  • 不同界面的Actions可能都想修改同一个状态

Vuex提供全局单例模式(大管家)工具来实现多界面状态管理。

Vuex状态管理

Vuex的基本思想:
将各界面共享的状态抽取出来,交给大管家,统一进行管理。之后,每个视图,按照规定好的规则,进行访问和修改等操作。
在这里插入图片描述

Vuex核心概念

State

state:单一状态树,用来保存共享状态。

优点:单一状态树能够以最直接的方式找到某个状态的片段,且在之后的维护和调试过程中,可以非常方便的管理和维护。
调用方式:$store.state.变量名

Getters

getters:类似计算属性。

参数
默认参数是state除此之外不能传递其他参数。

如果希望传递其他参数,则getters需要返回一个函数

functionName1(state){
  return functionName2(参数1, 参数2,……){
  ……
  }
}

调用方法:$store.getters.方法名(参数)

Mutation

mutation:状态更新

Vuex的store状态更新的唯一方式:提交mutation

mutation主要包括两部分:

  1. 字符串的事件类型
  2. 一个回调函数,该回调函数的第一个参数是state。
传递参数:
  • 默认参数state
  • payload,mutation的载荷,可以携带的额外参数。payload可以是一个对象,当需要多个参数时,就可以存放在payload内
提交风格
  • $store.commit('mutation方法名称')
  • $store.commit('mutation方法名称', payload)
  • $store.commit('mutation方法名称', {属性1, 属性2})
响应规则
  • 提前在store中初始化好所需的属性
  • 需要给state中的对象添加新属性时:
    • 方式一: 使用Vue.set(state中对象名称, ‘新属性名称’, 属性值)
    • 方式二: 用新对象给旧对象重新赋值
  • 需要删除state中的对象的某属性时:
    • vue.delete(对象名,‘要删掉的属性名称’)

只有满足响应规则,Vue组件才会在state中的数据发生改变时自动更新

类型常量

使用常量替代mutations事件的类型,避免方法过多时导致错误

  • 定义mutations-types.js文件
  • 在mutations-types.js文件中
    • 定义常量(mutations的方法名)export const XXX = mutations的方法名
  • mutation.js文件中
    • 引入 import{xxx} from 'mutations-types
    • 使用:[方法对应的常量名称]

在需要调用mutations的方法的组件中
引入 import{xxx} from 'mutations-types'

同步函数

一般Vuex要求mutation中的方法必须是同步方法。

主要原因:使用devtools时, devtools可以帮助捕捉mutation的快照。
如果是异步操作,devtools不能很好的追踪这个操作什么时候会被完成。出现的效果为页面内容会改变,但state中的变量内容不会变。

Action

Action:在Vuex中进行异步操作,如网络请求等

基本使用

定义action方法:

actions: {
	//方法一
	xxx(context) {
		context.commit('matution方法名')
	}
	
	//方法二
	xxx(context, payload) {
		context.commit('matution方法名', payload)
	}
	
	//方法三
	xxx(context, payload) {
		context.commit('matution方法名', payload.某属性名)
	}
	//方法四
	xxx(context) {
		return new Promise((resolve,reject) =>  {
			//异步操作
			……
			{
				……
				resolve()或reject()
			}
			……
		})
	}
}

context(上下文):一个具有与store对象相同方法和属性的对象。即可以通过context进行获取context.state、commit等相关操作

在组件中调用action

//方法一
this.$store.dispatch('action方法名')

//方法二
this.$store.dispatch('action方法名', payload)

//方法三
this.$store.dispatch('action方法名', {
	属性1: 'xxx',
	属性2: 'xxx'
	……
})

//方法四
this.$store.dispatch('action方法名').then(() => {
	…… 
})

Module

modules:划分模块

当需要管理的状态很多时,可以将store分割成多个模块(Module),而每个模块拥有自己的state、mutations、actions、getters等

局部状态

state

const state = {
	属性1: 'xxx',
	属性2: 'xxx',
	……
}

mutations

const mutations = {
	[常量(事件类型)](state) {
		……
	},
	……
}

getters

//第一个state是当前模块下的state
//第二个getters是当前模块下定义的getters
//第三个rootstate是全局state
const getters = {
	xxx1(state,getters,rootstate){
		……
	}
}

actions

const actions = {
	xxx1(context) {
		……
	},
	//参数含义同getters相似
	xxx2({state, commit, rootstate}) {
		……
	}
	
}

const moduleA = new Vuex.Store({
	state,
	getters,
	mutations,
	getters
})

在全局的store中

const store = new Vuex.Store({
	……
	modules: {
		moduleA,
		moduleB,
		……
	}
})

devtools:Vue开发的一个浏览器插件,跟踪同步操作

Vuex的基本使用

vuex目录结构

在这里插入图片描述

store文件夹中index.js文件的主要结构和内容

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

import getters from './getters'
import mutations from './mutations'
import actions from './actions'
// 安装插件
Vue.use(Vuex)
// 创建state对象,存放数据
const state = {
  name: 'withlan',
  book: {
    bname: '白夜行',
    price: 50,
    num: 3
  }
}
// 创建store对象,存放state、getters、mutations、actions属性
const store = new Vuex.Store({
  state,
  getters,
  mutations,// 同步操作
  actions,// 异步操作
  modules: {

  }
})

export default store

在main.js文件中导入store。其他Vue组件中,就可以通过this.$store获取到store对象

在这里插入图片描述
component文件夹中home.vue文件

<template>
  <div>
    <h4>-----------Home内容----------</h4>
    
  </div>

</template>

<script>
export default {
  name: "home"
}
</script>

<style scoped>
</style>

App.vue文件

<template>
  <div id="app">
    <h4>-----------App内容----------</h4>
    
    <home/>
  </div>
</template>

<script>
import Home from './components/home'

export default {
  name: 'App',
  components:{
    Home
  }
}
</script>

<style>
</style>

效果:
在这里插入图片描述

state的使用

获取state对象

App.vue

<template>
  <div id="app">
    <h4>-----------App内容----------</h4>
    <p>{{getState}}</p>
    
    <home/>
  </div>
</template>
……
computed: {
    getState(){
      return this.$store.state
    }
  },
……

home.vue

<template>
  <div>
    <h4>-----------Home内容----------</h4>
    <p>{{this.$store.state}}</p>
  </div>
</template>
……

效果:
在这里插入图片描述

获取state中的某数据
通过this.$store.state.属性名的方式来访问状态

App.vue

<template>
  <div id="app">
    <h4>-----------App内容----------</h4>
    <p>{{getState}}</p>
    <p>价格:{{getPrice}}</p>
    
    <home/>
  </div>
</template>
……
computed: {
    ……
    getPrice(){
      return this.$store.state.book.price
    }
  },

home.vue

<template>
  <div>
    <h4>-----------Home内容----------</h4>
    ……
    <p>价格:{{this.$store.state.book.price}}</p>
  </div>
</template>

效果:

在这里插入图片描述

getters的使用

只有参数state

getters.js

export default {
  currencyChange(state) {
    return state.book.price / 6
  }
}

在App.vue组件内调用

……
<p>白夜行的价格为:{{'¥' + getPrice + '≈ $' + priceChange}}</p>
……
priceChange(){
  return this.$store.getters.currencyChange
}
……

在home.vue组件内调用

……
<p>换算为美元为:{{'$' + this.$store.getters.currencyChange}}</p>
……

在这里插入图片描述

参数state、getters

getters.js

……
getBillInfo(state, getters) {
  return '需要支付' + getters.currencyChange + '美元'
 }
  ……

App.vue

……
<p>{{getBill}}</p>
……
getBill(){
      return this.$store.getters.getBillInfo
    }
……

home.vue

……
<p>{{this.$store.getters.getBillInfo}}</p>
……

效果:

在这里插入图片描述

mutations的使用

只有参数state

mutations.js文件

export default {
  increment(state){
    state.book.price++
  },
  decrement(state){
    state.book.price--
  }
}

App.vue文件夹

……
<p>价格:{{getPrice}}</p>
<button @click="add">price++</button>
<button @click="sub">price--</button>
……
methods:{
  add(){
   this.$store.commit('increment')
  },
  sub(){
    this.$store.commit('decrement')
  },
}
……

效果:

在这里插入图片描述
在这里插入图片描述

参数state、payload

mutations.js文件

export default {
……
  mulPrice(state,payload){
    state.book.price *= payload.discount
  }
}

App.vue文件

……
<p>
  discount:<input type="text" v-model="discount">
</p>
<button @click="mult">price*discount</button>
……
mult(){
      const discount = this.discount
      this.$store.commit('mulPrice', {
        discount
      })
    }

初始效果:
在这里插入图片描述
点击按钮:

在这里插入图片描述
在这里插入图片描述

使用常量替代 Mutation 事件类型

mutations-types.js文件

export const DECREMENT = 'decrement'
export const INCREMENT = 'increment'
export const MULPRICE = 'mulPrice'

mutations.js文件

import {INCREMENT, DECREMENT, MULPRICE} from './mutations-types'
export default {
  // increment(state){
  //   state.book.price++
  // },
  // decrement(state){
  //   state.book.price--
  // },
  [INCREMENT](state) {
    state.book.price++
  },
  [DECREMENT](state) {
    state.book.price--
  },
  [MULPRICE](state,payload){
    state.book.price *= payload.discount
  }
}


效果:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

actions的使用

只有参数context

actions.js

import {INCREMENT, DECREMENT, MULPRICE} from './mutations-types'
export default {
  aIncrement(context) {
    setTimeout(() => {
      context.commit(INCREMENT)
    }, 1000)
  },
  aDecrement(context) {
    setTimeout(() => {
      context.commit(DECREMENT)
    }, 1000)
  }

App.vue组件

……
<p>price:{{getPrice}}</p>
<button @click="aPriceAdd">action++</button>
<button @click="aPriceSub">action--</button>
……
aPriceAdd(){
 this.$store.dispatch('aIncrement')
},
aPriceSub(){
  this.$store.dispatch('aDecrement')
},
……

初始效果:
在这里插入图片描述
点击action++
在这里插入图片描述
点击action–
在这里插入图片描述

参数context、payload

actions.js

……
aMulPrice(context, payload) {
  setTimeout(() => {
    context.commit(MULPRICE, payload)
  }, 1000)
}
……

App.vue组件

<p>price:{{getPrice}}</p>
……
<button @click="aPriceMul">action*discount</button>
……
aPriceMul(){
  const discount = this.discount
  this.$store.dispatch('aMulPrice', {
    discount
  })
},
……

在这里插入图片描述

使用new Promise处理

actions.js

……
 // aMulPrice(context, payload) {
 //   setTimeout(() => {
 //     context.commit(MULPRICE, payload)
 //   }, 1000)
 // }
 aMulPrice(context, payload) {
   return new Promise((resolve, reject) => {
     setTimeout(() => {
       context.commit(MULPRICE, payload)
       resolve(payload)
     }, 1000)
   })
 }

App.vue

……
<p>price:{{getPrice}}</p>
……
<button @click="aPriceMul">action*discount</button>
……
aPriceMul(){
  const discount = this.discount
  // this.$store.dispatch('aMulPrice', {
  //   discount
  // })
  this.$store.dispatch('aMulPrice', {
    discount
  }).then(data => {
    console.log('今天书店打' + data.discount * 10 + '折')
  })
},
……

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值