vue实践04之vuex
vuex是一个专门为vue.js设计的集中式状态管理架构。我把它理解为在data中的属性需要共享给其他vue组件使用的部分,就叫做状态。简单的说就是data中需要共用的属性。比如:我们有几个组件要显示用户名称和用户等级,或者显示用户的地理位置。如果我们不把这些属性设置为状态,那每个组件遇到后,都会到服务器进行查找计算,返回后再显示。在中大型项目中会有很多共用的数据,所以尤大神给我们提供了vuex。
vuex把组件的共享状态抽取出来,以一个全局单例模式管理。在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为。通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,我们的代码将会变得更结构化且易维护。
适用场景
虽然 Vuex 可以帮助我们管理共享状态,但也附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的global event bus参考链接 就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
开始
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
-
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
-
你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
简单例子
- 引入vuex
npm install vuex --save需要注意的是这里一定要加上 --save,因为你这个包我们在生产环境中是要使用的。 - 新建一个vuex目录,在目录中新建store.js文件
state 对应定义的状态值, mutations 内封装对状态值改变的函数。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
add(state) {
state.count++;
},
reduce(state) {
state.count--;
}
}
})
export default store
- 在components目录中新建count.vue文件
<template>
<div>
<h2>{{msg}}</h2>
<hr/>
<h3>{{$store.state.count}}</h3>
<div>
<button @click="$store.commit('add')">+</button>
<button @click="$store.commit('reduce')">-</button>
</div>
</div>
</template>
<script>
import store from '@/vuex/store'
export default{
data(){
return{
msg:'Hello Vuex',
}
},
store
}
</script>
注意上述代码中的两个store.commit事件。
- 通过提交 mutation 的方式,而非直接改变 store.state.count,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。
- 由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。
- 在路由文件router/index.js文件中新增count的路由
{
path: '/count',
component: Count
}
- 访问页面http://127.0.0.1:8080/#/count
- 测试下vuex在不同component之间共享数据 在App.vue文件中同样也引用store,App.vue 代码如下:
<script>
import store from '@/vuex/store'
export default {
name: "App",
methods:{
goback(){
this.$router.go(-1);
},
goHome(){
this.$router.push('/');
}
},
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
//(需调用 Vue.use(Vuex))
store
};
</script>
注意上述代码中引入了store选项,这个选项可以把store的实例注入所有的子组件。
在App.vue文件template标签里增加显示state的代码如下:
<h3>{{$store.state.count}}</h3>
再次访问count页面,可以发现改变count组件的state值,App组件也会同步更新。
- 误区-组件共享
笔者试着同时打开两个count页面,天真以为改变一个页面的计数值,另一个页面也会自动变化,实际这是肯定不行的。vue就是一个单页应用,多页数据共享只能依赖与后端server的交互才能实现。vuex解决的时候单页中的多component共享数据的问题。
vue组件获得state
- 通过computed计算属性直接赋值
computed:{
computeCount(){
return this.$store.state.count;
}
}
显示调用代码为<h4>{{computeCount}}</h4>
完整代码如下:
<template>
<div>
<h2>{{msg}}</h2>
<hr/>
<h3>{{$store.state.count}}</h3>
<h4>{{computeCount}}</h4>
<div>
<button @click="$store.commit('add')">+</button>
<button @click="$store.commit('reduce')">-</button>
</div>
</div>
</template>
<script>
import store from '@/vuex/store'
export default{
data(){
return{
msg:'Hello Vuex',
}
},
computed:{
computeCount(){
return this.$store.state.count;
}
},
store
}
</script>
- 通过mapState的对象来赋值
当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。
- 首先要用import引入mapState
import {mapState} from 'vuex'-然后还在computed计算属性里写如下代码
computed: mapState({
// 箭头函数可使代码更简练
computeCount: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
count2完整代码如下:
<template>
<div>
<h2>{{msg}}</h2>
<hr/>
<h3>{{$store.state.count}}</h3>
<h4>{{computeCount}}</h4>
<h5>{{countAlias}}</h5>
<h6>{{countPlusLocalState}}</h6>
<div>
<button @click="$store.commit('add')">+</button>
<button @click="$store.commit('reduce')">-</button>
</div>
</div>
</template>
<script>
import store from '@/vuex/store'
import {mapState} from 'vuex'
export default{
data(){
return{
msg:'Hello Vuex',
localCount:20
}
},
computed: mapState({
// 箭头函数可使代码更简练
computeCount: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
}),
store
}
</script>
- 通过mapState的数组来赋值
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])
完整代码如下:
<template>
<div>
<h2>{{msg}}</h2>
<hr/>
<!--<h3>{{$store.state.count}}</h3>-->
<h6>{{count}}</h6>
<div>
<button @click="$store.commit('add')">+</button>
<button @click="$store.commit('reduce')">-</button>
</div>
</div>
</template>
<script>
import store from '@/vuex/store'
import {mapState} from 'vuex'
export default{
data(){
return{
msg:'Hello Vuex',
}
},
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
]),
store
}
</script>