what
Vuex 是专门为 Vue.js 设计的状态管理库,采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简单地说就是采用全局单例模式,将组件的共享状态抽离出来管理,使组件树中的每一个位置都可以获取共享的状态(变量)或者触发行为。
额,或者更直白的说就是响应式的全局变量
vuex核心概念
State
共享状态(即变量)
Getter
基于state的派生状态,可理解为组件中的计算属性
Mutation
更改vuex的store中状态的唯一方法,通过提交mutation修改状态,同步操作(规则上是不允许异步操作的,虽然异步也可以执行,但是对devtool调试的状态跟踪或多个状态更改操作相互依赖是很不好的,所以不要觉得只要不报错我就可以这么用,还是尽量按照规则来比较好)
Action
类似mutation,不同之处,1.通过提交mutation修改状态 2.支持异步操作
Module
模块,在大型项目中为了方便状态的管理和协作开发将store拆分为多个子模块(modules),每个子模块拥有完整的state、mutation、action、getter
vuex安装与使用
1.安装vuex(命令)
npm install vuex --save // 如果安装了淘宝镜像,可以使用 cnpm安装
安装成功
2.新建vuex文件和index.js
index.js文件内容
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
})
在根实例中注册store,mian.js文件中注册store
import Vue from 'vue'
import App from './App.vue'
import store from './vuex/index' // 引入index.js
Vue.config.productionTip = false
new Vue({
store, // 全局挂载store,注册后,子组件中可以使用this.$store访问
render: h => h(App),
}).$mount('#app')
3.store中变量的定义、管理、派生(getter)
①state–状态
我们在state中定义属性name,给一个初始值 “张三”;在store实例中注册state;在组件中使用$store.state.name使用定义的属性name
在组件中打印store,得到的结果
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
},
mounted() {
console.log('this.$store=====>',this.$store)
},
}
</script>
运行程序,在页面上显示出了我们定义的name属性,这里我们使用的是this.$store.state来访问vuex中定义的变量,还有一种辅助函数mapState的方式,我们在后边再讲解。好了,我们继续,定义了状态,我们就要对状态进行管理,去更改状态的值,就是下面要说的mutation。
②mutation–更改store中状态的唯一方法
之所以说唯一,是因为vuex中规定只能通过提交mutation的方式去更改store中的状态,包括action中的操作,也是通过提交mutation去修改。
另外一点就是vuex中规定mutation中不能包含异步操作,这个在上面的核心对象中已有简单介绍。
继续我们的操作,直接上代码:
vuex中index.js新增修改state的mutations方法
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const state = {
name: '张三'
}
// 修改state中的数据
const mutations = {
// 不带参数的方式
changeNameToZhangsan(state) {
state.name = '李四'
},
// 带参数的方式
changeNameWithParam(state, payload) {
state.name = payload.name
}
}
export default new Vuex.Store({
state,
mutations
})
app.vue中修改
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<div>store中的姓名:{{this.$store.state.name}}</div>
<div>
<button @click="setNameDefault">不带参数设置李四</button>
</div>
<div>
<input v-model="name"/><button @click="setName">带参数设置李四</button>
</div>
</div>
</template>
methods: {
setNameDefault() {
this.$store.commit('changeNameToZhangsan');
},
setName() {
// 带参数
this.$store.commit({
type:'changeNameWithParam',
name:this.name
});
}
},
在store.js的mutations中我们定义了两个方法,一个是不带参数更新name,一个是带参数更新name。
在示例组件的methods中,我们定义了两个方法,一个是调用无参的mutation方法,一个是调用带参的mutation方法(包含两种方式【载荷】、【对象】)
总结如下:
✦.mutation下事件的调用是通过 this.$store.commit 传入对应的type调用,关于辅助函数 mapMutations 的使用在后面一期讲解
✦.mutation下事件的定义分为无参的和有参的两种形式
✦.mutation事件的调用有两种形式,载荷和对象
③anction–异步更改状态
上面在mutation的介绍中我们提到了,mutation中规则上是不允许异步操作的,那如果我们需要异步的进行更改状态怎么办?于是vuex为我们提供了action。
先上代码:
1.定义actions并导出
const actions = {
// 无参
changeNameAsync(context) {
// 示例说明我们可以通过context.state来获取store中的变量
let _name = context.state.name;
console.log(_name)
// 异步更新
setTimeout(() => {
context.commit('changeNameToZhangsan');
}, 1000)
},
// 有参
changeNameWithParamAsync(context, payload) {
// 异步更新
setTimeout(() => {
context.commit('changeNameWithParam', payload);
}, 1000)
}
}
export default new Vuex.Store({
state,
mutations,
actions
})
组件中template
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<div>store中的姓名:{{this.$store.state.name}}</div>
<div>
<button @click="setNameDefault">不带参数设置李四</button>
</div>
<div>
<input v-model="name"/><button @click="setName">带参数设置李四</button>
</div>
<div>异步</div>
<div>store中的姓名:{{this.$store.state.name}}</div>
<div>
<button @click="setNameDefaultAsync">不带参数设置李四</button>
</div>
<div>
<input v-model="asyncName"/><button @click="setNameAsync">带参数设置李四</button>
</div>
</div>
</template>
methods中调用:
<script>
import HelloWorld from './components/HelloWorld.vue'
import { mapActions } from 'vuex';
console.log('mapActions===>',mapActions);
export default {
name: 'App',
components: {
HelloWorld
},
data(){
return {
name:'',
asyncName:''
}
},
mounted() {
console.log('this.$store=====>',this.$store)
},
methods: {
// 同步
setNameDefault() {
this.$store.commit('changeNameToZhangsan');
},
setName() {
this.$store.commit({
type:'changeNameWithParam',
name:this.name
});
},
// 异步
setNameDefaultAsync() {
this.$store.dispatch('changeNameAsync');
},
setNameAsync() {
this.$store.dispatch({
type:'changeNameWithParamAsync',
name:this.asyncName
});
},
...mapActions([
'changeNameAsync',
'changeNameWithParamAsync'
])
},
}
</script>
根据效果,总结得到:
1.action 与 mutation 除了使用了异步操作和调用mutation,其它使用并无差别
2.异步操作时,可以明显的看到我们设置的信息在延迟1s后发生改变
总结如下:
✦.action中不能直接更改状态,它是通过提交mutation来实现操作
✦.它的参数是一个与 store 实例具有相同方法和属性的 context 对象,所以可以通过context.state来获取store中的状态,可以通过context.commit来提交更改等
✦.action的调用使用 $.store.dispatch
✦.action事件的定义分为有参和无参两种
✦.action事件的触发同样可以使用载荷和对象两种方式
④getter–store中state的派生状态
getter,我们可以理解为是对store中state的一些派生状态,也可以理解为一种计算属性,因为它像计算属性一样,返回值会根据它的依赖被缓存起来,且依赖对象发生改变的时候它才会被重新计算。
getter的使用对我来讲就是将对store中某个属性相同的处理操作抽出出来,做了一个公共的处理
例如,我们使用store中name的时候,需要做一个判断:如果是张三,返回“张三最棒”,其它的原样返回,我们可以这样做
页面上将 this.$store.state.name 改为 formatterName即可。但是,如果我们这个操作不只这一个组件使用,那我们就需要在每一个组件中去定义这样一个计算属性,且不说代码冗余的问题,后期如果要更新后缀,想想吧,挨着一个一个组件的去巴拉。
而使用getter则可以很好的解决这个问题,不仅代码简洁,也利于维护,不多说,上getter使用代码:
总结如下:
✦.getter类似计算属性,是对store中state的一些派生状态,可以简化代码、便于维护
✦.getter的使用方法 $.store.getters.属性名
✦.getter的定义可以带参可以不带参
⑤关于module,它是对store的一个分割,将store分割成一个个小的模块,每个模块中又具有store完整的功能。他的使用主要面向一些状态非常多的大型应用,关于module的使用在这就不讲了,有兴趣可以看一下官网的介绍和讲解,官网module介绍。
4.vuex辅助函数的使用说明:
vuex的辅助函数有:mapState、mapMutations、mapGetters、mapActions
它们的使用我们可以配合ES6的展开运算符将其与局部计算属性或方法混合使用。