Vuex?是一个专为?Vue.js?应用程序开发的状态管理模式?+?库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
这个状态自管理应用包含以下几个部分:
状态,驱动应用的数据源;
视图,以声明方式将状态映射到视图;
操作,响应在视图上的用户输入导致的状态变化。
以下是一个表示“单向数据流”理念的简单示意:
但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
多个视图依赖于同一状态。
来自不同视图的行为需要变更同一状态。
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
Vuex就是把组件的共享状态抽取出来,以一个全局单例模式管理?。
通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。
npm install vuex@next --save
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {},
actions: {},
})
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import store from "./store";
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
components: { App },
template: '<App/>',
store
})
<template>
<div>
<my-addition></my-addition>
<p>----------------------------------</p>
<my-subtraction></my-subtraction>
</div>
</template>
<script>
import Addition from "./components/Addition";
import Subtraction from "./components/Subtraction";
export default {
data() {
return {};
},
components: {
'my-addition': Addition,
'my-subtraction': Subtraction
}
}
</script>
<style>
</style>
<template>
<div>
<!-- 组件访问State中数据的第一种方式 -->
<h3>当前最新的count值为:{{$store.state.count}}</h3>
<button>+1</button>
</div>
</template>
<script>
export default {
name: "Addition",
data() {
return {};
}
}
</script>
<style scoped>
</style>
<template>
<div>
<h3>当前最新的count值为:{{$store.state.count}}</h3>
<button>-1</button>
</div>
</template>
<script>
export default {
name: "Subtraction",
data() {
return {};
}
}
</script>
<style scoped>
</style>
Vuex?使用单一状态树,用一个对象就包含了全部的应用层级状态。
它便作为一个“唯一数据源?(SSOT)”而存在。这也意味着,每个应用将仅仅包含一个?store?实例。
$store.state.count
// 组件访问State中数据的第二种方式--引入
import {mapState} from 'vuex'
// 将全局数据,映射为当前组件的计算属性
...mapState(['count'])
// 模版中使用
<template>
<div>
<h3>当前最新的count值为:{{count}}</h3>
<button>-1</button>
</div>
</template>
Vuex?中的?mutation?非常类似于事件:每个?mutation?都有一个字符串的事件类型?(type)和一个回调函数?(handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受?state?作为第一个参数
使用commit触发mutation
// store.js
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
state.count++;
}
},
actions: {},
})
// Adition.vue
<template>
<div>
<!-- 组件访问State中数据的第一种方式 -->
<h3>当前最新的count值为:{{$store.state.count}}</h3>
<button @click="addHandler">+1</button>
</div>
</template>
<script>
export default {
name: "Addition",
data() {
return {};
},
methods: {
addHandler() {
// 这种方式是不合法的!!!
// this.$store.state.count++;
// 触发mutation的第一种方式
this.$store.commit('add')
}
}
}
</script>
<style scoped>
</style>
带参数的mutation
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
state.count++;
},
// 第二位就是,传入的参数。如果是多个参数可以传入对象
addN(state, step) {
state.count += step;
},
sub(state) {
state.count--;
},
},
actions: {},
})
// Addition.vue
<template>
<div>
<!-- 组件访问State中数据的第一种方式 -->
<h3>当前最新的count值为:{{$store.state.count}}</h3>
<button @click="addHandler">+1</button>
<button @click="addNHandler">+N</button>
</div>
</template>
<script>
export default {
name: "Addition",
data() {
return {};
},
methods: {
addHandler() {
// 这种方式是不合法的!!!
// this.$store.state.count++;
// 触发mutation的第一种方式
this.$store.commit('add')
},
addNHandler() {
this.$store.commit('addN', 3)
}
}
}
</script>
<style scoped>
</style>
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
state.count++;
},
addN(state, step) {
state.count += step;
},
sub(state) {
state.count--;
},
subN(state, step) {
state.count -= step;
},
},
actions: {},
})
Subtraction.vue
<template>
<div>
<h3>当前最新的count值为:{{count}}</h3>
<button @click="subHandler">-1</button>
<button @click="subNHandler">-N</button>
</div>
</template>
<script>
// 组件访问State中数据的第二种方式--引入
import {mapState, mapMutations} from 'vuex'
export default {
name: "Subtraction",
data() {
return {};
},
computed: {
// 将全局数据,映射为当前组件的计算属性
...mapState(['count'])
},
methods: {
...mapMutations(['sub', 'subN']),
// 将全局mutations函数映射为组件中的函数
subHandler() {
this.sub();
},
subNHandler() {
this.subN(3)
}
}
}
</script>
<style scoped>
</style>
四、Action
Action的作用是执行异步函数,因为mutation中无法执行异步函数。在mutation中使用异步函数会导致state的状态无法被及时追踪导致代码异常!!!
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
// setTimeout(() => {
// // 不要在mutations中执行异步操作,会造成vuex存储状态不可追踪
// state.count++;
// }, 1000)
state.count++;
},
addN(state, step) {
state.count += step;
},
sub(state) {
state.count--;
},
subN(state, step) {
state.count -= step;
},
},
actions: {
// context: 可以理解为当前的Vuex.Store实例对象
addAsync(context) {
setTimeout(() => {
// 在 actions 中,不能直接修改 state 中的数据
// 必须通过 context.commit() 触发某个 mutations 才行
context.commit('add')
}, 1000)
},
addNAsync(context, step) {
setTimeout(() => {
// 在 actions 中,不能直接修改 state 中的数据
// 必须通过 context.commit() 触发某个 mutations 才行
context.commit('addN', step)
}, 1000)
}
},
})
<template>
<div>
<!-- 组件访问State中数据的第一种方式 -->
<h3>当前最新的count值为:{{$store.state.count}}</h3>
<button @click="addHandler">+1</button>
<button @click="addNHandler">+N</button>
<button @click="addHandlerAsync">+1 Async</button>
<button @click="addNHandlerAsync">+N Async</button>
</div>
</template>
<script>
export default {
name: "Addition",
data() {
return {};
},
methods: {
addHandler() {
// 这种方式是不合法的!!!
// this.$store.state.count++;
// 触发mutation的第一种方式
this.$store.commit('add')
},
addNHandler() {
this.$store.commit('addN', 3)
},
addHandlerAsync() {
// 这里的 dispatch 专门用来触发action函数
this.$store.dispatch('addAsync')
},
addNHandlerAsync() {
// 这里的 dispatch 专门用来触发action函数
this.$store.dispatch('addNAsync', 3)
}
}
}
</script>
<style scoped>
</style>
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
// setTimeout(() => {
// // 不要在mutations中执行异步操作,会造成vuex存储状态不可追踪
// state.count++;
// }, 1000)
state.count++;
},
addN(state, step) {
state.count += step;
},
sub(state) {
state.count--;
},
subN(state, step) {
state.count -= step;
},
},
actions: {
// context: 可以理解为当前的Vuex.Store实例对象
addAsync(context) {
setTimeout(() => {
// 在 actions 中,不能直接修改 state 中的数据
// 必须通过 context.commit() 触发某个 mutations 才行
context.commit('add')
}, 1000)
},
addNAsync(context, step) {
setTimeout(() => {
// 在 actions 中,不能直接修改 state 中的数据
// 必须通过 context.commit() 触发某个 mutations 才行
context.commit('addN', step)
}, 1000)
},
subAsync(context) {
setTimeout(() => {
context.commit('sub')
}, 1000)
},
subNAsync(context, step) {
setTimeout(() => {
context.commit('subN', step)
}, 1000)
}
},
})
<template>
<div>
<h3>当前最新的count值为:{{count}}</h3>
<button @click="subHandler">-1</button>
<button @click="subNHandler">-N</button>
<button @click="subHandlerAsync">-1 Async</button>
<button @click="subNHandlerAsync">-N Async</button>
</div>
</template>
<script>
// 组件访问State中数据的第二种方式--引入
import {mapState, mapMutations, mapActions} from 'vuex'
export default {
name: "Subtraction",
data() {
return {};
},
computed: {
// 将全局数据,映射为当前组件的计算属性
...mapState(['count'])
},
methods: {
...mapMutations(['sub', 'subN']),
...mapActions(['subAsync', 'subNAsync']),
// 将全局mutations函数映射为组件中的函数
subHandler() {
this.sub();
},
subNHandler() {
this.subN(3)
},
subHandlerAsync() {
this.subAsync();
},
subNHandlerAsync() {
this.subNAsync(3)
}
}
}
</script>
<style scoped>
</style>
Vuex?允许我们在?store?中定义“getter”(可以认为是?store?的计算属性)。就像计算属性一样,getter?的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
// setTimeout(() => {
// // 不要在mutations中执行异步操作,会造成vuex存储状态不可追踪
// state.count++;
// }, 1000)
state.count++;
},
addN(state, step) {
state.count += step;
},
sub(state) {
state.count--;
},
subN(state, step) {
state.count -= step;
},
},
actions: {
// context: 可以理解为当前的Vuex.Store实例对象
addAsync(context) {
setTimeout(() => {
// 在 actions 中,不能直接修改 state 中的数据
// 必须通过 context.commit() 触发某个 mutations 才行
context.commit('add')
}, 1000)
},
addNAsync(context, step) {
setTimeout(() => {
// 在 actions 中,不能直接修改 state 中的数据
// 必须通过 context.commit() 触发某个 mutations 才行
context.commit('addN', step)
}, 1000)
},
subAsync(context) {
setTimeout(() => {
context.commit('sub')
}, 1000)
},
subNAsync(context, step) {
setTimeout(() => {
context.commit('subN', step)
}, 1000)
}
},
getters: {
showNum(state) {
return '当前最新的数量是【' + state.count + '】';
}
}
})
<template>
<div>
<!-- 组件访问State中数据的第一种方式 -->
<h3>{{$store.getters.showNum}}</h3>
<button @click="addHandler">+1</button>
<button @click="addNHandler">+N</button>
<button @click="addHandlerAsync">+1 Async</button>
<button @click="addNHandlerAsync">+N Async</button>
</div>
</template>
<script>
export default {
name: "Addition",
data() {
return {};
},
methods: {
addHandler() {
// 这种方式是不合法的!!!
// this.$store.state.count++;
// 触发mutation的第一种方式
this.$store.commit('add')
},
addNHandler() {
this.$store.commit('addN', 3)
},
addHandlerAsync() {
// 这里的 dispatch 专门用来触发action函数
this.$store.dispatch('addAsync')
},
addNHandlerAsync() {
// 这里的 dispatch 专门用来触发action函数
this.$store.dispatch('addNAsync', 3)
}
}
}
</script>
<style scoped>
</style>
<template>
<div>
<h3>{{showNum}}</h3>
<button @click="subHandler">-1</button>
<button @click="subNHandler">-N</button>
<button @click="subHandlerAsync">-1 Async</button>
<button @click="subNHandlerAsync">-N Async</button>
</div>
</template>
<script>
// 组件访问State中数据的第二种方式--引入
import {mapState, mapMutations, mapActions, mapGetters} from 'vuex'
export default {
name: "Subtraction",
data() {
return {};
},
computed: {
// 将全局数据,映射为当前组件的计算属性
...mapState(['count']),
// 将全局的getters,映射为当前组件的计算属性
...mapGetters(['showNum'])
},
methods: {
...mapMutations(['sub', 'subN']),
...mapActions(['subAsync', 'subNAsync']),
// 将全局mutations函数映射为组件中的函数
subHandler() {
this.sub();
},
subNHandler() {
this.subN(3)
},
subHandlerAsync() {
this.subAsync();
},
subNHandlerAsync() {
this.subNAsync(3)
}
}
}
</script>
<style scoped>
</style>
Vuex?允许我们将?store?分割成模块(module)。每个模块拥有自己的?state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态