原文
大纲
1、什么是Vuex
2、什么是“状态管理模式”?
3、什么情况下应该使用 Vuex?
4、Vuex和全局变量的概念区别
5、最简单的store
6、Vuex的简单使用
6.1、vue文件版本
6.2、js文件版本
1、什么是Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
2、什么是“状态管理模式”?
让我们从一个简单的 Vue 计数应用开始:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
这个状态自管理应用包含以下几个部分:
1、state,驱动应用的数据源;
2、view,以声明方式将 state 映射到视图;
3、actions,响应在 view 上的用户输入导致的状态变化。
以下是一个表示“单向数据流”理念的极简示意:
问题暴露
但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
1、多个视图依赖于同一状态。
2、来自不同视图的行为需要变更同一状态。
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
解决方式
因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!
另外,通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,我们的代码将会变得更结构化且易维护。
这就是 Vuex 背后的基本思想,借鉴了 Flux、Redux、和 The Elm Architecture。与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。
3、什么情况下应该使用 Vuex?
虽然 Vuex 可以帮助我们管理共享状态,但也附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 global event bus 就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
4、Vuex和全局变量的概念区别
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
1、Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
2、你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
5、最简单的store
/*
安装 Vuex 之后,让我们来创建一个 store。创建过程直截了当——仅需要提供一个初始
state 对象和一些 mutation:
*/
// 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
/*
现在,你可以通过 store.state 来获取状态对象,以及通过 store.commit 方法触发
状态变更:
*/
store.commit('increment')
console.log(store.state.count) // -> 1
/*
再次强调,我们通过提交 mutation 的方式,而非直接改变 store.state.count,是因
为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你
在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一
些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般
的调试体验。
由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算
属性中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。
*/
6、Vuex的简单使用
下载Vuex
vue-cli搭建的项目
利用脚手架搭建项目之后,可以通过npm引入vuex
npm install vuex
也可以直接通过引入文件
<script src="/path/to/vue.js"></script>
<script src="/path/to/vuex.js"></script>
实例:基本的Vuex计数应用
vue文件版本
1、创建store状态管理
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 状态
const state = {
count: 0
}
const mutations = {
INCREMENT (state) {
state.count++
},
DECREMENT (state) {
state.count--
}
}
const actions = {
increment: ({ commit }) => commit('INCREMENT'),
decrement: ({ commit }) => commit('DECREMENT')
}
const getters = {
moreCount: function () {
return state.count + 'more'
}
}
export default new Vuex.Store({
state,
getters,
actions,
mutations
})
2、引入store状态管理
import Vue from 'vue'
import App from './App'
import store from './store'
Vue.config.productionTip = false
new Vue({
el: '#app',
store,
components: { App },
template: '<App/>'
})
3、使用store进行状态管理——计数
3.1、相应点击事件发布(dispatch)状态
<template>
<div>
<div>{{ msg }}</div><br>
<div>
<button @click="increment()">+</button>
<button @click="decrement()">-</button>
</div><br>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'HelloWorld'
}
},
methods: {
increment () {
console.log('+++')
this.$store.dispatch('increment')
},
decrement () {
console.log('---')
this.$store.dispatch('decrement')
}
}
}
</script>
<style scoped>
</style>
3.2、接收状态改变之后的值
<template>
<div>
{{ msg }}<br>
{{ count }}<br>
{{ moreCount }}
</div>
</template>
<script>
export default {
name: 'SimpleCounter',
data () {
return {
msg: 'SimpleCounter'
}
},
computed: {
count () {
return this.$store.state.count
},
moreCount () {
return this.$store.getters.moreCount
}
}
}
</script>
<style scoped>
</style>
js文件版本
<html>
<meta charset="utf-8">
<head>
<title>Test</title>
</head>
<body>
<div id="app">
<p>{{ count }}</p>
<p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</p>
</div>
<script src="./vue.js"></script>
<script src="./vuex.js"></script>
<script src="./vuex-demo2.js"></script>
</body>
</html>
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment: state => state.count++,
decrement: state => state.count--
}
})
new Vue({
el: '#app',
computed: {
count () {
return store.state.count
}
},
methods: {
increment () {
store.commit('increment')
},
decrement () {
store.commit('decrement')
}
}
})