Vuex它是专门为Vue.js应用程序设计的状态管理工具。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。通俗来讲当有很多组件,组件与组件之间需要进行相互通信,这时就可以用到Vuex保存多个组件的共享状态,这时只需求state就可以在组件之间同步状态了。
vuex的特点是把数据单独的隔离形成一棵树状图,单独隔离这就说明它有自己的生态系统,其中action作为数据输入,state作为数据输出。只能在mutations里修改state,actions不能直接修改state。在mutations中修改state的数据,它只能是同步操作,不能存在异步的操作(但如果是异步也不会报错,只是不建议)。如果是异步的操作,那可以把操作放在action中,拿到数据再通过mutations同步进行处理。下面我们来详细分析vuex的用法及核心概念。
一、基本使用
1、安装 npm install vuex --save
2、在src目录下新建一个vuex的目录store并进行资源的引入,通过Vue.use()安装
import Vue from 'Vue'
import Vuex from 'vuex'
Vue.use(Vuex)
3、安装vuex后,创建一个store.
export default new Vuex.Store({
state:{count:100}
})
4、为了在vue组件中访问,vuex提供了一个从根组件向所有子组件,以store选项的方式“注入”store
new Vue({
el:'#app',
store:store,
})
每一个vuex应用的核心就是store(仓库)。store它就是一个容器,包含着应用中大部分的state(状态)。vuex的状态存储是响应式的,当vue组件从store中读取state,state发生变化 ,那相应的组件也会变化更新。不能直接的改变仓库中的state,改变的唯一方法就是显示的提交mutation.这样可以方便跟踪每一个状态的变化。来面来分别介绍这些概念。
二、核心概念
1、state
vuex使用单一状态树,用一个对象包含全部的应用层级状态。state它是这一状态管理工具唯一的数据源,所有的数据都存储在这里面。这说明了,每个应用将仅仅包含一个store实例。那如何在vue组件中展示呢?由于vuex是响应式的,最简单的方法是在计算属性中返回某个状态。
创建一个名show的组件
<template>
<div>
<h1>{{this.$store.state.count}}</h1>
</div>
</template>
<script>
export default{
name: 'show',
computed:{
count(){
return this.$store.state.count
}
}
}
</script>
<style lang='less'></style>
当this.$store.state.count发生变化时,都会重新计算属性,触发更新相关联的DOM.
如果一个组件需求获取的状态有多个时,如果把这些状态都声明为计算属性,那会很冗余,所以我们可以用发mapState辅助函数来帮助计算属性,mapstate其实就是把state中的属性映射到computed中。在使用它之前先在进行引入。如下所示:
index.js
export default new Vuex.Store({
state: {
//这里放置的是公用数据
count:100,
from:'china'
}
})
-------------------------------------------
show.vue
<template>
<div>
<h1>{{count}}</h1>
<h3>{{sex}}</h3>
<h3>{{from}}</h3>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "show",
data() {
return {
str: "国籍"
};
},
computed: mapState({
//第一种写法
count: "count", //当名字一样时
//第二名写法,使用箭头函数
sex: state => state.from,
//第三种写法,使用普通函数(this指向实例)
from: function(state) {
return this.str + ":" + state.from;
}
})
};
</script>
<style lang='less'></style>
当然computed不会因为引入了mapState辅助函数而失去原有的功能,但mapstate函数返回的是一个对象,这两者要如何混合使用呢?这时,需求使用一个工具函数将多个对象合并为一个,将最终对象传给computed属性。如下所示:
computed: {
localComputed () {
//......
},
// 使用对象展开运算符将此对象混入到外部对象中
...mapState({
// ......
})
}
2、getter
有时候需求从store中的state中派生出一些状态,所以要用到getter,getter是store的计算属性,getter返回的值会会根据依赖被缓存起来,只有当它的依赖值发生改变才会被重新计算。它的使用方法是 $store.getters.属性名。它可带参数,也可以不带参数,接受state为其第一个参数,也可以接受其它getter作为第二个参数。
//index.js
export default new Vuex.Store({
state: {
list: [1, 2, 3, 5, 6, 7, 8, 9, 0]
},
getters: {
fillteredList: (state, getters)=>{
console.log(state,getters);
return state.list.filter(item => item > 5)
}
}
})
--------------------------------
//show.vue
<template>
<div></div>
</template>
<script>
// import { mapState } from "vuex";
export default {
name: "show",
computed:{
list(){
return this.$store.getters.filteredList;
}
}
};
</script>
<style lang='less'>
</style>
我们可以通过mapGetters辅助函数将store中的getter映射到局部计算属性,使用前还是要进行引入。
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
//如果想将一个getter属性另取一个名字,使用对象形式
doneCout:'donetodoCount'
// ...
])
}
}
3、mutation
我们知道更改vuex中store状态的唯一方法就是提交mutation.vuex中的mutation类似于事件,每个mutation都有一个字符串的事件类型(type)和一个回调函数(handler),这个回调函数就是实际进行状态更改的地方,可以看成是{type:handler()},调用type时用到store.commit方法。
//index.js
export default new Vuex.Store({
state: {
//这里放公用的数据
count: 100,
age: 20
},
mutations: {
//这里面的函数都是用来修改state中状态的
//用this.$store.commit()方法触发
changeCount(state, option) {
//state是我们公用状态,option是执行这个函数时传递的值,最多只有两个参数,最少为一个
console.log(state, option);
console.log(arguments);
// state.count += option;
state.count += option.num;
}
}
})
-----------------------------------------------
button.vue
<template>
<div>
<button @click="add">增加</button>
<button @click="minus">减少</button>
<h2>{{res}}</h2>
</div>
</template>
<script>
export default {
name: "xxx",
computed:{
res(){
return this.$store.state.count;
}
},
//在methods中提交
methods: {
add() {
//commit()执行会默认去mutations中查找对应的函数并执行
// this.$store.commit("changeCount",10);
//还可以改变风格
this.$store.commit({type:'changeCount',num:100})
},
minus() {
// this.$store.commit("changeCount", -10);
this.$store.commit({type:'changeCount',num:-100})
}
}
};
</script>
<style lang='less'></style>
需求注意的是mutation必须是同步的,这是为了调试方便。在项目中使用时,一般将常量放在单独的文件中,这样更有助于协作开发,提高效率。
//mutation-type.js
export const some_mutation = 'some_mutation'
//store.js
import Vuex from 'vuex'
import { some_mutation } from './mutation-type'
let store = new Vuex.Store({
state: {......},
mutations: {
[some_mutation](state, option) {
//......
}
}
})
可以使用mapMutations辅助函数。它的作用是把mutations中的方法映射到组件的methods中。使用辅助函数之前还是要先引入,如下所示:
<template>
<div>
<button @click="add(30)">增加</button>
<button @click="minus">减少</button>
<button @click="changeCount(50)">test</button>
<h2>{{res}}</h2>
</div>
</template>
<script>
import {mapMutations} from 'vuex'
export default {
name: "xxx",
computed:{
res(){
return this.$store.state.count;
}
},
methods: {
...mapMutations(['changeCount']),
...mapMutations({add:'changeCount'}),
// add() {
// this.$store.commit('changeCount',100);
// },
minus() {
this.$store.commit("changeCount", -10);
}
}
};
</script>
<style lang='less'></style>
4、action
actions是为异步操作而设置的。因为mutations只能是同步操作,但在实际的项目中会存在异步操作,这样就变成了在action中去提交mutation,然后在组件的methods中去提交action。只在在提交的时候使用的是dispatch函数,而mutations使用的是commit函数.
actions:{
//这里面的函数最终都会触发mutations中的函数
//这些函数中原则上不能直接修改state中的数据
//通过this.$store.dispatch()
changeCount(store, option){
//console.log(store, option);
setTimeout(() => {
store.commit('changeCount',option)
}, 200);
// console.log(arguments)
}
}
--------------------------------------
methods: {
add() {
this.$store.dispatch('changeCount',100,200);
},
minus() {
this.$store.dispatch('changeCount',-100);
}
}
5、module
由于vuex使用了单一状态树,应用的所有状态都会集中到一个比较大的对象中,当应用比较复杂的时候,store对象就会变的十分的臃肿。所以才有了module,它可以将store分割成模块,每个模块有自己的state,getter,mutation,action甚至是嵌套的子模块,从上到下进行同样方式的分割。它主要是进行模块的划分。
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 的状态
对于模块内部的mutation和getter来说,接受的第一个参数是模块的局部状态state。根结点的状态为rootState。
总结一下,对于含有vuex的项目来说,其结构大体如下:1、应用层级的状态都应该集中在store2、提交mutation是更改state的唯一方式 3、异步操作应试放在action里。如果store文件过大,可以将action,mutation,getter进行分割,分割到单独的文件中。