官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
状态管理到底是什么?
等等,如果是这样的话,为什么官方还要专门出一个插件Vuex呢?难道我们不能自己封装一个对象来管理吗?
但是,有什么状态时需要我们在多个组件间共享的呢?
OK,从理论上理解了状态管理之后,让我们从实际的代码再来看看状态管理。
我们先来看看但界面的状态管理吧.
我们知道,要在单个组件中进行状态管理是一件非常简单的事情
这图片中的三种东西,怎么理解呢?
写点代码,加深理解:
<template>
<div class="test">
<div>当前计数:{{counter}}</div>
<button @click="counter+=1">+1</button>
<button @click="counter-=1">-1</button>
</div>
</template>
<script>
export default {
name: 'HellowWorld',
data() {
return {
counter: 0
}
}
}
</script>
Vue已经帮我们做好了单个界面的状态管理,但是如果是多个界面呢?
也就是说对于某些状态(状态1/状态2/状态3)来说只属于我们某一个试图,但是也有一些状态(状态a/状态b/状态c)属于多个试图共同想要维护的
全局单例模式(大管家)
首先将个人练习时使用的项目文件放在这儿vuex,需要则可以下载对照观看
我们还是实现一下之前简单的案例
首先,我们需要在某个地方存放我们的Vuex代码:
import Vue from "vue";
import Vuex from 'vuex';
//安装插件
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
counter: 1000,
},
mutations: {
increment(state) {
state.counter++;
},
decrement(state){
state.counter--;
},
incrementCount(state, count) {
state.counter += count;
}
}
})
//导出
export default store
其次,我们让所有的Vue组件都可以使用这个store对象
import Vue from 'vue'
import App from './App'
import store from "./store"
new Vue({
el: '#app',
store,
render: h => h(App)
})
<template>
<div id="app">
<p>{{counter}}</p>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
</div>
</template>
<script>
export default {
name: "App",
components: {
},
computed: {
counter() {
return this.$store.state.counter;
}
},
methods: {
increment() {
this.$store.commit('increment');
},
decrement(){
this.$store.commit('decrement');
}
}
}
</script>
<style scoped>
</style>
好的,这就是使用Vuex最简单的方式了。
我们来对使用步骤,做一个简单的小节:
this.$store.state.
属性的方式来访问状态this.$store.commit('mutation中方法')
来修改状态注意事项:
Vuex有几个比较核心的概念:
下面对它进行一一介绍.
Vuex提出使用单一状态树, 什么是单一状态树呢?
但是,它是什么呢?我们来看一个生活中的例子。
这个和我们在应用开发中比较类似:
有时候,我们需要从store中获取一些state变异后的状态,比如下面的Store中:
获取学生年龄大于2的个数。
const store = new Vuex.Store({
state: {
counter: 1000,
students: [
{id: 110, name: 'wht', age: 1},
{id: 113, name: 'cod', age: 0},
{id: 112, name: 'bad', age: 4},
{id: 114, name: 'sff', age: 2}
],//每一个属性都有个Dep -> [watcher] Dep 观察属性发生变化,自动通知所有界面进行响应式改变数据
},
我们可以在Store中定义getters
computed: {
morestu() {
return this.$store.state.students.filter(s => {
return s.age >= 2
})
}
},
morestu(state) {
return state.students.filter(s => s.age >= 2);
},
如果我们已经有了一个获取所有年龄大于2岁学生列表的getters, 那么代码可以这样来写
getters: {
morestu(state) {
return state.students.filter(s => s.age >= 2);
},
morestuLength(state, getters) {
return getters.morestu.length;
},
getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数.
getters: {
morestu(state) {
return state.students.filter(s => s.age >= 2);
},
morestuLength(state, getters) {
return getters.morestu.length;
},
moreAge(state){
// return function (age) {
// return state.students.filter(s => s.age > age);
// }
return age => {
return state.students.filter(s => s.age > age);
}
}
},
Vuex的store状态的更新唯一方式:提交Mutation
Mutation主要包括两部分:
字符串的事件类型(type)
一个回调函数(handler),该回调函数的第一个参数就是state。
mutation的定义方式:
mutations: {
//利用mutation修改state的参数内容
//方法,方法有默认的参数state
increment(state) {
state.counter++;
},
},
通过mutation更新
increment() {
this.$store.commit('increment')
},
在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数
Mutation中的代码:
incrementCount(state, count) {
state.counter += count;
},
addCount(count) {
//payLoad: 参数被称为是负载
//普通的提交风格
this.$store.commit('incrementCount', count)
},
上面的通过commit进行提交是一种普通的方式
Vue还提供了另外一种风格, 它是一个包含type属性的对象
//特殊 的提交风格 count 会变成一个对象
this.$store.commit({
type: 'incrementCount',
count: count
});
Mutation中的处理方式是将整个commit的对象作为payload使用, 所以代码没有改变, 依然如下:
incrementCount(state, count) {
state.counter += count;
},
Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新.
这就要求我们必须遵守一些Vuex对应的规则:
我们来看一个例子:
如何才能让它改变呢?
我们来考虑下面的问题:
如何避免上述的问题呢?
具体怎么做呢?
//mutation-type.js
export const INCREMENT = 'increment'
//index.js
mutations: {
//利用mutation修改state的参数内容
//方法,方法有默认的参数state
[INCREMENT](state) { //利用常量写法
state.counter++;
},
通常情况下, Vuex要求我们Mutation中的方法必须是同步方法.
主要的原因是当我们使用devtools时, 可以devtools可以帮助我们捕捉mutation的快照.
但是如果是异步操作, 那么devtools将不能很好的追踪这个操作什么时候会被完成.
比如我们之前的代码, 当执行更新时, devtools中会有如下信息:
但是, 如果Vuex中的代码, 我们使用了异步函数:
你会发现state中的info数据一直没有被改变, 因为他无法追踪到.
So, 通常情况下, 不要再mutation中进行异步的操作
我们强调, 不要再Mutation中进行异步操作.
Action的基本使用代码如下:
context是什么?
这样的代码是否多此一举呢?
mutations: {
//利用mutation修改state的参数内容
//方法,方法有默认的参数state
[INCREMENT](state) { //利用常量写法
state.counter++;
},
},
actions: {
//context: 上下文
increment(context) {
context.commit('increment');
}
},
在Vue组件中, 如果我们调用action中的方法, 那么就需要使用dispatch
methods: {
increment() {
this.$store.dispatch('increment')
}
}
同样的, 也是支持传递payload
前面我们学习ES6语法的时候说过, Promise经常用于异步操作.
OK, 我们来看下面的代码:
actions: {
//context: 上下文
aUpdateInfo(context) {
//定义异步操作
// setTimeout(() => {
// context.commit('updateinfo');
// console.log(payload.message);
// payload.success();
// }, 1000)
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('updateinfo');
console.log(payload);
resolve('111111');
}, 1000)
})
}
},
methods: {
updateinfo() {
// this.$store.commit('updateinfo')
// this.$store.dispatch('aUpdateInfo', {
// message: '我是携带的信息',
// success: () => {
// console.log('里面已经完成了');
// }
// });
this.$store
.dispatch('aUpdateInfo', '我是携带的信息')
.then(res => {
console.log('里面完成了提交');
console.log(res);
})
},
Module是模块的意思, 为什么在Vuex中我们要使用模块呢?
我们按照什么样的方式来组织模块呢?
//创建对象
const moduleA = {
state: {
name: 'zhangsan'
},
mutations: {
updateName(state, payload) {
state.name = payload
}
},
getters: {
fullName(state) {
return state.name + '1111';
},
fullName2(state, getters) {
return getters.fullName + '22222';
},
fullName3(state, getters, rootState) {
return getters.fullName2 + rootState.counter;
}
},
actions: {
aUpdateName(context) {//context的commit
setTimeout(() => {
context.commit('updateName', 'wanghwu')
}, 1000)
}
},
modules: {}
}
const moduleB = {
state: {
.....
},
mutations: {
.....
},
getters: {
.....
},
actions: {
.....
},
modules: {}
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
虽然, 我们的各种方法都是定义在对象内部的.
但是在调用的时候, 依然是通过this.$store来直接调用的.
actions的写法呢? 接收一个context参数对象
const moduleA = {
//,,,
actions: {
increment({ state, commit, rootState}) {
if((state.count + rootState.count) % 2 === 1) {
commit('increment');
}
}
}
}
如果getters中也需要使用全局的状态, 可以接受更多的参数
const moduleA = {
//,,,
getters: {
sum({ state, commit, rootState}) {
return state.count + rootState.count;
}
}
}