简介
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简单来说,用来集中管理数据,类似于React中的Redux,都是基于Flux的前端状态管理框架
基本用法
以官方例子为例:
当点击+1按钮的时候,count 加1,点击-1按钮的时候,count 减1.
安装vuex
cnpm install vuex -S
创建store.js文件,在main.js中导入并配置store.选项
import store from './store.js'
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
编辑store.js文件
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
//数据
var state = {
count: 10
}
//获取数据
var getters = {
count(state){
return state.count;
}
}
//异步动作
const actions = {
add({commit,state}){
commit('add');
},
sub({commit,state}){
commit('sub');
}
}
//同步动作
const mutations = {
add(state){
state.count++;
},
sub(state){
state.count--;
}
}
const store = new Vuex.Store({
state,
getters,
actions,
mutations
})
export default store;
Vuex的核心是Store(仓库),相当于是一个容器,一个store实例中包含以下属性的方法:
state 定义属性(状态、数据)
getters 用来获取属性
actions 定义方法(动作)
commit 提交变化,修改数据的唯一方式就是显式的提交mutations
mutations 定义变化
注:不能直接修改数据,必须显式提交变化,目的是为了追踪到状态的变化
编辑App.vue
在子组件中访问store对象的两种方式:
- 通过this.$store访问
通过mapState、mapGetters、mapActions访问
mapState 获取state mapGetters 获取getters mapActions 获取actions
<template>
<div id="app">
<p>{{count}}</p>
<button @click="add">+</button>
<button @click="sub">-</button>
</div>
</template>
<script>
import {mapGetters,mapActions} from 'vuex'
export default {
name: 'app',
data() {
return {
}
},
// computed:{ //方式一
// count(){
// return this.$store.state.count;
// }
// },
computed:{
...mapGetters([
'count'
]),
msg(){
console.log("同时使用mapGetters和自定义computed");
}
},
//computed:mapGetters([
// 'count'
//]),
methods:mapActions([
'add',
'sub'
])
}
</script>
<style>
a {
text-decoration: none;
}
</style>
探究
我们再看一张图
很多人会疑惑:
为什么要有actions和mutations?
我为什么要在action里调用mutations然后由mutations去修改state?
为什么不可以在mutations里直接去修改state?
官方的说法是:不能直接修改数据,必须显式提交变化,目的是为了追踪到状态的变化
我们也经常听到actions是异步动作,mutations是同步动作,那么该怎么去理解呢?
我举一个例子,也许你就会豁然开朗!
假如你是一个君王,前方正在打仗。每隔一段时间前线就来一个探子汇报战况:
“报!!! 我们还有1500军队,弟兄们快顶不住了!!”
这就是getters的作用了
<span>我军军队:{{count}}</span>
computed:mapGetters([
'count'
]),
var getters = {
count(state){
return state.count;
}
}
于是你慌了,你命令道:
“不行,赶快撤军,速度跑路!!”
<button @click="chejun">撤军</button>
methods: {
chejun(){
this.$store.dispatch("chejun");
}
}
于是探子把你的命令传给了前线元帅。
元帅看到你的圣旨上两个大字:“撤军”
他会怎么做?会立马撤军吗?
不会吧,他要进行各方面的准备和筹划吧!**
这就是actions的作用了
//异步动作 进行逻辑判断,异步请求,错误处理
const actions = {
chejun({commit,state}){
console.log("今晚开始撤军!!");
if(state.count >= 10000){
console.log("赵四你带3000人掩护");
},
else if(state.count >= 5000){
console.log("赵四你带500人掩护");
},
else if(state.count >= 1000){
console.log("赵四你带50人摆空城记");
}
console.log("其他人随我出城!!");
commit('chejun');
}
}
元帅的命令下来了以后,士兵们执行就好了
这就是matations的作用了
//同步动作 立马就去执行
const mutations = {
chejun(state){
state.count = 0;
}
}
例子虽然不太恰当,但是你应该明白为什么要有actions和mutations了吧。为什么叫actions异步,mutations同步了吧。
进阶
明白了vuex的基本用法和逻辑结构后,我们再探讨下vuex的项目结构
项目结构
Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
- 应用层级的状态应该集中到单个 store 对象中。
- 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
- 异步逻辑都应该封装到 action 里面
只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块
我们把上面的例子改进一下
这是项目结构图
子模块
user.js
/**
* 用户模块
*/
import types from '../types.js' //从types.js中引用常量
const state={
count:6
}
var getters={
count(state){
return state.count;
}
}
const actions = {
increment({commit,state}){
commit(types.INCREMENT); //提交一个名为increment的变化,名称可自定义,可以认为是类型名
},
decrement({commit,state}){
if(state.count>10){
commit(types.DECREMENT);
}
}
}
const mutations={
[types.INCREMENT](state){
state.count++;
},
[types.DECREMENT](state){ //es6语法:[]内的视为常量
state.count--;
}
}
export default {
state,
getters,
actions,
mutations
}
公共actions
actions.js
import types from './types.js'
const actions={
incrementAsync({commit,state}){
//异步操作
var p=new Promise((resolve,reject) => {
setTimeout(() => {
resolve();
},3000);
});
p.then(() => {
commit(types.INCREMENT);
}).catch(() => {
console.log('异步操作');
});
}
}
export default actions;
公共getters
getters.js
const getters={
isEvenOrOdd(state){
return state.user.count%2==0?'偶数':'奇数';
}
}
export default getters;
常量模块
type.js
/**
* 定义类型常量
*/
const INCREMENT='INCREMENT'
const DECREMENT='DECREMENT'
export default {
INCREMENT,
DECREMENT
}
根模块
index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
import getters from './getters.js'
import actions from './actions.js'
import user from './modules/user.js'
export default new Vuex.Store({
getters,
actions,
modules:{ //这里存放的是子模块
user
}
});