Vuex 学习笔记
概要
官方解释Vuex是一个专为Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。状态?我把它理解为在data中的属性需要共享给其他vue组件使用的部分,就叫做状态。简单的说就是data中需要共用的属性。比如:我们有几个页面要显示用户名称和用户等级,或者显示用户的地理位置。如果我们不把这些属性设置为状态,那每个页面遇到后,都会到服务器进行查找计算,返回后再显示。在中大型项目中会有很多共用的数据,所以尤大神给我们提供了vuex。
在一些应用比较复杂的单页面项目里使用vuex比较合适,因为当兄弟、父子组件数据通信、共用状态过多时这些状态会变得让你难以管理,当某部分出现问题便要用大量时间去组件中查改代码,这并不是我们开发者想要的,因此用一个vuex来集中管理各个状态是比较好的选择。
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
- 1.Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 2.你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit)
mutation
。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
使用
一、创建
- 1.下载
npm install vuex -s
- 引入安装与使用
// main.js
import Vue form 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
var store = new Vuex.Store({ //store对象
state:{
show:false
}
})
new Vue({
el: '#app',
data(){},
router,
store, //使用store
render: h => h(App)
})
这是最直接的 vuex
的使用,为了方便 , 我们把 store 对象写在了 main.js 里面 , 但实际上为了便于日后的维护 , 我们分开写更好 , 我们在 src 目录下 , 新建一个 store 文件夹 , 然后在里面新建一个 index.js
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state:{
show:false
}
})
此时,main.js 就应该写成这样
// main.js
import store from './store'
import Vue from 'vue'
new Vue({
el: '#app',
data(){},
router,
store, //使用store
render: h => h(App)
})
此时,所有的子组件可以通过 $store.state.show
来调用状态值 show
这样就把 store 分离出去了 , 那么还有一个问题是 : 这里 $store.state.show 无论哪个组件都可以使用 , 那组件多了之后 , 状态也多了 , 这么多状态都堆在 store 文件夹下的 index.js 不好维护怎么办 ? 使用 vuex 的 modules
我们把其作为一个状态文件分离出去
// components/dialog_store.js
export default {
state:{
show:false
}
}
再把 store 文件夹下的 index.js 改成 :
import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex);
import dialog_store from '../components/dialog_store.js'; //引入某个store对象
export default new vuex.Store({
modules: {
dialog: dialog_store
}
})
这时,在组件需要使用这个状态时,把使用方法 this.$store.state.show
改写为 this.$store.state.dialog.show
即可。
如果还有其他的组件需要使用 vuex , 就新建一个对应的状态文件 , 然后将他们加入 store 文件夹下的 index.js 文件中的 modules 中。
modules: {
dialog: dialog_store,
other: other, //其他组件
}
二、访问
上面我们已经创建好了const state
,这个就是我们说的访问状态对象,它就是我们SPA(单页应用程序)中的共享值。现在我们要把这个const state
赋值给内部对象,也就是把 components/dialog_store.js
中的值赋值给我们模板里data中的值。
- 1.通过computed的计算属性直接赋值
computed:{
show(){
return this.$store.state.dialog.show
}
}
- 2.通过mapState的对象来赋值
<template>
<el-dialog :visible.sync="show"></el-dialog>
</template>
<script>
import {mapState} from 'vuex';
export default {
computed:{
//这里的三点叫做 : 扩展运算符
...mapState({
show:state=>state.dialog.show
}),
}
}
</script>
相当于
<template>
<el-dialog :visible.sync="show"></el-dialog>
</template>
<script>
import {mapState} from 'vuex';
export default {
computed:{
show(){
return this.$store.state.dialog.show;
}
}
}
</script>
- 3.通过mapState的数组来赋值
computed:mapState(["show"])
三、修改状态 (Mutations)
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
// components/dialog_store.js
export default {
state:{//state
show:false
},
mutations:{
switch_dialog(state){//这里的state对应着上面这个state
state.show = state.show?false:true;
//你还可以在这里执行其他的操作改变state
}
}
}
而使用的组件则是通过 $store.commit
方法来修改状态,像下面这样使用 $store.commit('switch_dialog')
来触发 mutations 中的 switch_dialog 方法。
<template>
<div id="app">
<a href="javascript:;" @click="$store.commit('switch_dialog')">点击</a>
<t-dialog></t-dialog>
</div>
</template>
<script>
import dialog from './components/dialog.vue'
export default {
components:{
"t-dialog":dialog
}
}
</script>
这里需要注意的是:
- 1.mutations 中的方法是不分组件的 , 假如你在
dialog_stroe.js
文件中的定义了switch_dialog
方法 , 在其他文件中的一个switch_dialog
方法 , 那么$store.commit('switch_dialog')
会执行所有的switch_dialog
方法。 - 2.mutations里的操作必须是同步的。
三、执行多个 mutations(action)
多个 state
的操作 , 使用 mutations
会来触发会比较好维护 , 那么需要执行多个 mutations
就需要用 action
了:
export default {
state:{ //state
show:false
},
mutations:{
switch_dialog(state){ //这里的state对应着上面这个state
state.show = state.show?false:true;
//你还可以在这里执行其他的操作改变state
}
},
actions:{
switch_dialog(context){ //这里的context和我们使用的$store拥有相同的对象和方法
context.commit('switch_dialog');
//你还可以在这里触发其他的mutations方法
},
}
}
也就是说,在一个 action
中可以一次执行多个 mutation
。这时,我们用 $store.dispatch
来触发 action
中的方法。官方推荐 , 将异步操作放在 action 中。
<template>
<div id="app">
<a href="javascript:;" @click="$store.dispatch('switch_dialog')">点击</a>
<t-dialog></t-dialog>
</div>
</template>
<script>
import dialog from './components/dialog.vue'
export default {
components:{
"t-dialog":dialog
}
}
</script>
四、getters
getters
和 vue
中的 computed
类似 , 都是用来计算 state
然后生成新的数据 ( 状态 ) 的。
假如我们需要一个与状态 show 刚好相反的状态 ,在使用时可以直接用 computed
这样算出来 :
computed(){
not_show(){
return !this.$store.state.dialog.show;
}
}
如果很多很多个组件中都需要用到这个与 show 刚好相反的状态 , 那么我们每次使用都要在组件里写 not_show , 很冗余,使用 getters
就可以解决这种问题 :
export default {
state:{ //state
show:false
},
getters:{
not_show(state){ //这里的state对应着上面这个state
return !state.show;
}
},
mutations:{
switch_dialog(state){ //这里的state对应着上面这个state
state.show = state.show?false:true;
//你还可以在这里执行其他的操作改变state
}
},
actions:{
switch_dialog(context){ //这里的context和我们使用的$store拥有相同的对象和方法
context.commit('switch_dialog');
//你还可以在这里触发其他的mutations方法
},
}
}
这样,在使用时,直接在组件中使用 $store.state.dialog.show
来获得状态 show , 使用
$store.getters.not_show
来获得状态 not_show