Vue学习-Vuex状态管理
可以理解为:多个组件共享的变量全部存储在一个对象里面,然后将这个对象放在顶层的Vue实例中,让其他组件可以使用---集中式状态管理
自己封装的对象不是响应式的(Vue.prototype.shareObj
)
官网: Vuex 是什么? | Vuex (vuejs.org)
管理什么状态?
-
比如用户的登录状态、用户名称、头像、地理位置信息等等
-
比如商品的收藏、购物车中的物理等等
-
这些状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的
Vuex-单界面到多界面的状态切换
单界面
多界面
Demo
index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// 存放共享的状态
state: {
counter: 1000
},
// 修改共享的状态方法
mutations: {
// 默认有参数state
increment(state) {
state.counter++;
},
decrement(state) {
state.counter--;
}
},
actions: {},
getters: {
},
modules: {}
})
App.vue
<template>
<div id="app">
<h2>{{ message }}</h2>
<h2>{{ $store.state.counter }}</h2>
<button @click="addition">+</button>
<button @click="substraction">-</button>
<hr>
<h2>HelloVuex</h2>
<hello-vuex></hello-vuex>
</div>
</template>
<script>
import HelloVuex from './components/HelloVuex';
export default {
name: 'App',
data() {
return {
message: '我是App组件'
}
},
components: {
HelloVuex
},
methods: {
addition() {
// 调用increment函数
this.$store.commit('increment');
},
substraction() {
// 调用decrement函数
this.$store.commit('decrement');
}
}
}
</script>
<style>
</style>
HelloVuex
<!-- -->
<template>
<div>
{{ this.$store.state.counter }}
</div>
</template>
<script>
export default {
data () {
return {
};
}
}
</script>
<style lang='stylus' scoped>
</style>
Vuex核心概念
State
单一状态树(单一数据源)
只创建一个store
对象
Getters (类似于计算属性)
基本使用
getters: {
powerCounter(state) {
return state.counter * state.counter;
}
}
{{ $store.getters.powerCounter }}
getters作为参数
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).length;
}
}
}
Mutations-状态更新
- Vuex的store状态的更新唯一方式:提交Mutation
- Mutation主要包括两部分:
- 字符串的事件类型(type)
- 一个回调函数(handler),该回调函数的第一个参数就是state
携带参数
// 修改共享的状态方法
mutations: {
// 默认有参数state
increment(state) {
state.counter++;
},
decrement(state) {
state.counter--;
},
// 携带参数
incrementCount(state, count) {
state.counter += count;
}
},
addCount(count) {
this.$store.commit('incrementCount', count)
}
提交风格
this.$store.commit({
type: 'incrementCount',
count: 5
})
incrementCount(state, payLoad) {
state.counter += payLoad.count;
}
Vuex数据的响应原理
-
Vuex的store中的state是响应式的,当state中的数据发生改变时,Vue组件会自动更新
-
要求state中的数据初始化过的,如果没有初始化,则不会响应式
-
info: { name: 'aaa', age: 40, height: 1.98 }
-
updateInfo(state) { // state.info.name = 'bbb'; // 响应式 state.info['address'] = 'chengdu'; // 非响应式,没有初始化 }
-
-
解决办法
Vue.set(state.info, 'address', 'chengdu');
-
删除对象属性
delete state.info.age; // 不是响应式 Vue.delete(state.info, 'age'); // 响应式
Actions-异步
异步方式修改state放在Actions,不要放在mutations中
actions: {
// context: 上下文
aUpdateInfo(context, payLoad) {
setTimeout(() => {
// mutations中的方法
context.commit('updateInfo');
console.log(payLoad);
}, 1000)
}
},
updateInfo() {
// this.$store.commit('updateInfo');
// 这里使用dispatch而不是commit
this.$store.dispatch('aUpdateInfo', '我是payLoad')
}
通知外部回调完成
方法1:
actions: {
// context: 上下文
aUpdateInfo(context, payLoad) {
setTimeout(() => {
context.commit('updateInfo');
console.log(payLoad);
console.log(payLoad.message);
payLoad.success();
}, 1000);
}
}
updateInfo() {
// this.$store.commit('updateInfo');
this.$store.dispatch('aUpdateInfo', {
message: '我是携带的信息',
success: () => {
console.log('里面的已经完成了')
}
})
}
优雅的方式2:
aUpdateInfo(context, payLoad) {
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('updateInfo');
console.log(payLoad);
// console.log(payLoad.message);
// payLoad.success();
resolve('success');
}, 1000);
})
}
updateInfo() {
// this.$store.commit('updateInfo');
// this.$store.dispatch('aUpdateInfo', {
// message: '我是携带的信息',
// success: () => {
// console.log('里面的已经完成了')
// }
// })
this.$store.dispatch('aUpdateInfo', '我是携带的信息')
.then((res) => {
console.log('外部' + res);
})
}
Modules
单一状态树只能创建一个store,modules可以创建不同的模块,不同的模块中可以创建store(套娃)
const moduleA = {
state: {
name: 'zhangsan'
},
mutations: {
updateName(state, payLoad) {
state.name = payLoad;
}
},
actions: {
aUpdateNameA(context) {
setTimeout(() => {
context.commit('updateName', 'wangwu')
}, 1000);
}
},
getters: {
fullname(state) {
return state.name + '1111';
},
fullname2(state, getters) {
return getters.fullname + '222';
},
fullname3(state, getters, rootstate) {
return getters.fullname2 + rootstate.counter;
}
}
}
const moduleB = {
state: {
name: 'lisi'
},
mutations: {
},
actions: {
},
getters: {
}
}
modules: {
a: moduleA,
b: moduleB
}
<h2>{{ $store.state.a.name }}</h2>
<h2>{{ $store.state.b.name }}</h2>
<h2>{{ $store.getters.fullname }}</h2>
<h2>{{ $store.getters.fullname2 }}</h2>
<h2>{{ $store.getters.fullname3 }}</h2>
<button @click="updateName">修改名字</button>
<button @click="aUpdateNameA">异步修改名字</button>
updateName() {
this.$store.commit('updateName', 'fawaikuangtu');
},
aUpdateNameA() {
this.$store.dispatch('aUpdateNameA');
}
项目结构
将index.js中的文件抽取为对应的模块,然后在index.js中导入对应的模块
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块