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主要包括两部分:
- 字符串的事件类型
- 一个回调函数,该回调函数的第一个参数是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的方法名
- 定义常量(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 + '折')
})
},
……