vuex
vuex是什么?Vuex是一个专为Vue.js应用程序开发的状态管理模式,其工作模式如图所示。刚开始这张图可能很难看懂,但是理解完vuex的五大核心概念(state,getters,mutations,actions,modules)以后,你应该会有自己的理解。
vuex使用需要挂载到vue实例上,这样就可以在所有组件中使用这些数据,所以我们将所有需要共享的data,使用vuex来统一管理。一般都将src下面的store文件夹中的index.js作为入口文件(文件夹及文件名自定义),然后在main.js中导入store对象。
每个应用将仅仅包含一个store实例。在根组件注册时,store实例时会被注入到根组件下面的所有组件。所有子组件都可以通过this.$store访问到。
import Vue from 'vue'
import App from './App'
import router from './router'
//sotre
import store from "./store";
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
State
可以看作为data中的属性,State中所有的属性都会被加到响应式系统中。即当监听到了属性的变化,就会通知所有用到这个属性的界面进行刷新。
我们在store文件夹下创建一个modules文件夹用来存放各个不同的模块,创建一个test文件夹及test.js用来测试。
test.js
const state = {
message:"Hello VUEX!"
};
export default {
state
};
修改index.js入口文件
index.js
import Vue from 'vue';
import Vuex from 'vuex';
import test from './modules/test/test';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
test
}
});
添加完成后就可以在vue中调用
App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<div>{{$store.state.test.message}}</div>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
并可以再调试工具中看到相应变量
Getters
有时候我们需要从state派生出来一些其他状态,类似于它的计算属性。
如我们需要将state.message字符串翻转的功能,我们无需在很多地方复制这个函数,也无需抽取到一个公共函数中然后在多处导入。我们只需要定义一个getter
。然后就和计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
test.js
const state = {
message: "Hello VUEX!"
};
const getters = {
reversedMessage: state => {
return state.message.split('').reverse().join('')
}
};
export default {
state,
getters
};
App.vue -> template
<template>
<div id="app">
<img src="./assets/logo.png">
<div>{{$store.state.test.message}}</div>
<div>{{$store.getters.reversedMessage}}</div>
</div>
</template>
Mutations
提交Mutation是更改store中状态的唯一方式。使用store.commit提交的时候可以传入额外的参数,称为mutation的载荷(payload)。
Mutation 必须是同步函数。当是异步函数的时候,当mutation触发了,回调函数可能还没被调用,所以在回调函数中的改变将无法追踪。
test.js
- 添加一个Mutation用来改变message的值。
const state = {
message: "Hello VUEX!"
};
const getters = {
reversedMessage: state => {
return state.message.split('').reverse().join('')
}
};
const mutations = {
changeMessage: (state, s = "") => {
state.message += s
}
};
export default {
state,
getters,
mutations
};
App.vue -> template
<template>
<div id="app">
<img src="./assets/logo.png">
<div>{{$store.state.test.message}}</div>
<div>{{$store.getters.reversedMessage}}</div>
<div>
<button v-on:click="handleClick('Hello!')">Say Hello!</button>
</div>
</div>
</template>
App.vue -> script
<script>
export default {
name: 'App',
methods: {
handleClick(s) {
this.$store.commit('changeMessage', s);
}
}
}
</script>
- 点击一次按钮后
Actions
如果处理异步操作就需要用到Action。Action不同于mutation,它提交的是mutation,而不是直接改变状态。
test.js
const state = {
message: "Hello VUEX!"
};
const getters = {
reversedMessage: state => {
return state.message.split('').reverse().join('')
}
};
const mutations = {
changeMessage: (state, s = "") => {
state.message += s
}
};
const actions = {
actionsChangeMessage: (context, s = "") => {
context.commit('changeMessage',s)
}
}
export default {
state,
getters,
mutations,
actions
};
App.vue -> template
<template>
<div id="app">
<img src="./assets/logo.png">
<div>{{$store.state.test.message}}</div>
<div>{{$store.getters.reversedMessage}}</div>
<div>
<button v-on:click="handleClick('Hello!')">Say Hello!</button>
<button v-on:click="handleActionsClick('Hi!')">Say Hi!</button>
</div>
</div>
</template>
App.vue -> script
<script>
export default {
name: 'App',
methods: {
handleClick(s) {
this.$store.commit('changeMessage', s);
},
handleActionsClick(s) {
this.$store.dispatch('actionsChangeMessage', s);
}
}
}
</script>
因为Action不是直接更改状态,而是提交mutation。所以我们就可以在Action内部执行异步操作。
const actions = {
actionsChangeMessage: (context, s = "") => {
setTimeout(() => {
context.commit('changeMessage',s)
}, 1000)
}
}
- 1s后触发
Modules
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter。
就如我们上面的module目录下的test文件夹,就可以当做一个子模块。
在vuex的入口文件配置如下即可:
export default new Vuex.Store({
modules: {
test:test
//, b: moduleB
}
});
辅助函数
vuex提供了辅助函数mapState、mapGetters、mapMutations、mapActions。将vuex.store中的属性映射到vue实例身上,这样就可以在vue实例中直接访问vuex.store中的属性。
mapState,mapGetters:将属性映射到computer上
mapMutations、mapActions:将属性映射到methods上
对应的写法改为:
App.vue:
<template>
<div id="app">
<img src="./assets/logo.png">
<div>{{message}}</div>
<div>{{reversed}}</div>
<div>
<button v-on:click="change('Hello!')">Say Hello!</button>
<button v-on:click="actionsChange('Hi!')">Say Hi!</button>
</div>
</div>
</template>
<script>
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
export default {
name: 'App',
computed: {
...mapState({
message: state => state.test.message
}),
...mapGetters({
reversed: 'reversedMessage'
})
},
methods: {
...mapMutations({
change: 'changeMessage'
}),
...mapActions({
actionsChange: 'actionsChangeMessage'
})
}
}
</script>
四个辅助函数的写法均有以下两种形式
...mapActions(['method1', 'method2' ]),
...mapActions({m1: 'method1',m2: 'method2', })