Vuex 是什么?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

State

Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。

Vuex 的状态存储是响应式的,我们可以通过计算属性 computedState -> Computed -> 触发dom更新

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
const store = new Vuex.Store({state:{msgList:[]}
})
new Vue({store,//...computed:{msgList:function(){return this.$store.state.msgList}}
})
复制代码

mapState 辅助函数

当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键

const store = new Vuex.Store({state:{count:0}
})
复制代码
<template><div><p>{{localCount}}</p></div>
</template><script>
import { mapState } from 'vuex'
export default {data(){return{countStr:'count:'}},computed:mapState({count: 'count',localCount(state){return this.countStr + state.count}})
}
</script>复制代码

Getter

Vuex 允许我们在 store 中定义getter(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

Getter 可以接收第一个参数是 state , 第二个参数是 getters,他可以依赖于stategetters计算,就跟组件的计算属性一样。通过 store.getters 访问。

可以让getter返回一个函数,实现对getter传参。

mapGetters 辅助函数

mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性

const store = new Vuex.Store({state:{id:0,count:0},getters:{countStr:state=>'getCountStr' + state.count,getValue:(state,getters) => value => getters.countStr + state.id + value},
})
复制代码
<template><div><input type="text" name="" v-model="value"  id=""><p>{{countStr}}</p><p>{{localGetCount}}</p></div>
</template><script>
import { mapGetters } from 'vuex'
export default {data(){return{value:'',localCountStr:'count:'}},computed:{...mapGetters(['countStr']),localGetCount(){return this.$store.getters.getValue(this.value)},}
}
</script>复制代码

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,Vuex 中的 mutation 类似于事件,每一个 mutation 都有一个字符串的事件类型,和一个回调函数,回调函数中接收的第一个参数是 state 。

mutation 的函数必须是一个同步函数

const store = new Vuex.Store({state:{msgList:[],id:0,count:0},mutations:{insertMsg(state,payload){var item = {id:state.id++,msg:payload.msg}state.msgList.push(item)state.count++}}
})
复制代码

我们不能直接调用一个 mutation handle ,我们可以通过 store.commit('insertMsg') 触发事件

对象风格的提交方式

提交 mutation 的另一种方式是直接使用包含 type 属性的对象

this.$store.commit({type:'insertMsg',msg:this.value
})
复制代码

Mutation 需遵守 Vue 的响应规则

  • 最好提前在你的 store 中初始化好所有所需属性。
  • 当需要在对象上添加新属性时,你应该
    • 使用 Vue.set(obj, 'newProp', 123), 或者
    • 新对象替换老对象。例如,利用 stage-3 的对象展开运算符我们可以这样写:
    state.obj = { ...state.obj, newProp: 123 }
    复制代码

Action

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

分发 Action

store.dispatch('increment')
// 以载荷形式分发
store.dispatch('incrementAsync', {amount: 10
})
// 以对象形式分发
store.dispatch({type: 'incrementAsync',amount: 10
})
复制代码

组件中分发 Action

你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store)

组合 Action

Action 通常是异步的,那么如何知道 action 什么时候结束呢?更重要的是,我们如何才能组合多个 action,以处理更加复杂的异步流程?

首先,你需要明白 store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise

const store = new Vuex.Store({state:{msgList:[],id:0,count:0},mutations:{insertMsg(state,payload){var item = {id:state.id++,msg:payload.msg}state.msgList.push(item)state.count++},},actions:{insertMsg({commit},payload){return new Promise((resolve,reject)=>{setTimeout(() => {if(Math.random() > 0.5){commit('insertMsg',payload);resolve();}else{reject('插入失败')}}, 1000);})}}
})
复制代码
<!--组件调用-->
<template><div><input type="text" name="" v-model="value"  id=""><button @click="handleInsert" >submit</button><p v-for="(item,index) in msgList" :key='index'>{{item.id}}:{{item.msg}}<button @click='handleRemove(item.id)'>del</button></p></div>
</template><script>
import { mapState } from 'vuex'
import { mapGetters } from 'vuex'
import { mapMutations } from 'vuex';
export default {data(){return{value:'',localCountStr:'count:'}},computed:{...mapState({msgList: state => state.msgList,count: 'count',localCount(state){return this.localCountStr + state.count}}),},methods:{handleInsert(payload){if(this.value !== ''){this.$store.dispatch({type:'insertMsg',msg:this.value}).then(()=>{//成功回调console.log('suss')this.value = '';},(error)=>{//失败回调alert(error)})}},}
}
</script>
复制代码

Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割

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 的状态
复制代码

命名空间

默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。

如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

const child = {namespaced: true,state:{count:0},getters:{logCount:state=>'child-log-count:'+state.count},mutations:{addCount(state,payload){let offset = payload.count - 0 || 1state.count += offset},reduceCount(state,payload){let offset = payload.count - 0 || 1state.count -= offset},logCount(state){console.log(`child-mutations-log-${state.count}`)}},actions:{add({commit,dispatch,getters,rootState},payload){return new Promise((resolve,reject)=>{setTimeout(() => {if(Math.random() > 0.3){commit('addCount',payload);dispatch('add',payload,{ root: true });commit('logCount');resolve();}else{commit('logCount');reject('添加失败')}}, 300);})},reduce({commit,dispatch,getters,rootState},payload){return new Promise((resolve,reject)=>{setTimeout(() => {if(Math.random() > 0.3){commit('reduceCount',payload);dispatch('reduce',payload,{ root: true });commit('logCount');resolve();}else{reject('添加失败')}}, 1000);})}},modules:{grandchild:{namespaced: true,state:{count:0},getters:{logCount:state=>'grandchild-log-count:'+state.count},mutations:{addCount(state,payload){let offset = payload.count - 0 || 1state.count += offset},reduceCount(state,payload){let offset = payload.count - 0 || 1state.count -= offset},logCount(state){console.log(`grandchild-mutations-log-${state.count}`)}},actions:{add({commit,dispatch,getters,rootState},payload){return new Promise((resolve,reject)=>{setTimeout(() => {if(Math.random() > 0.3){commit('addCount',payload);dispatch('child/add',payload,{ root: true });commit('logCount');resolve();}else{commit('logCount');reject('添加失败')}}, 300);})},reduce({commit,dispatch,getters,rootState},payload){return new Promise((resolve,reject)=>{setTimeout(() => {if(Math.random() > 0.3){commit('reduceCount',payload);dispatch('child/reduce',payload,{ root: true });commit('logCount');resolve();}else{reject('添加失败')}}, 1000);})}},}}
}
const store = new Vuex.Store({namespaced: true,state:{count:0},getters:{logCount:state=>'parent-log-count:'+state.count},mutations:{addCount(state,payload){let offset = payload.count - 0 || 1state.count += offset},reduceCount(state,payload){let offset = payload.count - 0 || 1state.count -= offset},logCount(state){console.log(`parent-mutations-log-${state.count}`)}},actions:{add({commit,dispatch,getters,rootState},payload){return new Promise((resolve,reject)=>{setTimeout(() => {if(Math.random() > 0.3){commit('addCount',payload);commit('logCount');resolve();}else{reject('添加失败')}}, 1000);})},reduce({commit},payload){return new Promise((resolve,reject)=>{setTimeout(() => {if(Math.random() > 0.5){commit('reduceCount',payload);commit('logCount');resolve();}else{commit('logCount');reject('添加失败')}}, 1000);})}},modules:{child}
})
复制代码
<template><div><p>demo9</p><input type="number" name="" v-model="value" id=""><button @click='add'>add</button><button @click='reduce'>reduce</button><p>grandchildCount : {{grandchildCount}}</p><p>{{grandchildLog}}</p><p>childCount : {{childCount}}</p><p>{{childLog}}</p><p>parentCount : {{parentCount}}</p><p>{{parentLog}}</p></div>
</template><script>
import { mapState, mapGetters, mapActions } from 'vuex'
export default {data(){return{value:1}},computed:{...mapState({grandchildCount:state=>{return state.child.grandchild.count},childCount:state=>{return state.child.count},parentCount:state=>{return state.count}}),...mapGetters({grandchildLog:'child/grandchild/logCount',childLog:'child/logCount',parentLog:'logCount'})},methods:{add(){this.$store.dispatch('child/grandchild/add',{count:this.value}).then(()=>{console.log('添加成功')},()=>{console.log('添加失败')})},reduce(){this.$store.dispatch('child/grandchild/reduce',{count:this.value}).then(()=>{console.log('减少成功')},()=>{console.log('减少失败')})},}
}
</script>
复制代码

项目结构

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   # 产品模块
复制代码

Vue-状态管理流程

转载于:https://juejin.im/post/5bc6fc9a6fb9a05d1658b826

少侠请重新来过 - Vue学习笔记(八) - Vuex相关推荐

  1. 少侠请重新来过 - Vue学习笔记(二) - Vue生命周期

    Vue 生命周期和钩子 每一个vue实例创建时,会经历一段初始化的过程,同时会调用其生命周期钩子,实例的钩子this指向它的Vue实例,不需要在钩子用箭头函数. <template>< ...

  2. Vue学习笔记(八) 组件进阶

    1.插槽 (1)插槽内容 像 HTML 元素一样,我们常常需要给组件传递内容 比如在下面的例子中,我们给自定义组件 prompt-info 传入 出错啦 的文本 <!DOCTYPE html&g ...

  3. Vue学习笔记进阶篇——Render函数

    本文为转载,原文:Vue学习笔记进阶篇--Render函数 基础 Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML.然而在一些场景中,你真的需要 JavaScript 的完全编 ...

  4. Vue学习笔记入门篇——数据及DOM

    本文为转载,原文:Vue学习笔记入门篇--数据及DOM 数据 data 类型 Object | Function 详细 Vue 实例的数据对象.Vue 将会递归将 data 的属性转换为 getter ...

  5. Vue学习笔记(六) 表单输入绑定

    v-model 指令在表单元素上创建双向数据绑定,它负责用于监听用户输入事件以更新数据 注意,v-model 会忽略所有表单元素特性的初始值,而总是将 Vue 实例的数据作为数据来源 1.输入框 &l ...

  6. Vue学习笔记进阶篇——多元素及多组件过渡

    本文为转载,原文:Vue学习笔记进阶篇--多元素及多组件过渡 多元素的过渡 对于原生标签可以使用 v-if/v-else.但是有一点需要注意: 当有相同标签名的元素切换时,需要通过 key 特性设置唯 ...

  7. Vue学习笔记02——Vue路由

    Vue学习笔记01--Vue开发基础 一.初识路由 1.路由的作用 Vue的路由是前端路由,可以让组件之间互相切换. 2.vue-router.js文件 Vue的路由使用需要引入vue-router. ...

  8. day4 vue 学习笔记 组件 生命周期 数据共享 数组常用方法

    系列文章目录 day1学习vue2笔记 vue指令 day2 学习vue2 笔记 过滤器 侦听器 计算属性 axios day3 vue2 学习笔记 vue组件 day4 vue 学习笔记 组件 生命 ...

  9. VUE学习笔记------奕长苏

    VUE学习笔记------奕长苏 一.引言 二.视图和数据的双向绑定 - - - v-model 三.组件 - - - component 四.其它 一.引言 本文为个人在学习vue时总结的学习笔记, ...

最新文章

  1. MySQL · 最佳实践 · 如何索引JSON字段
  2. 【转载保存】RunTime.getRunTime().addShutdownHook 添加钩子
  3. THOR:MindSpore 自研高阶优化器源码分析和实践应用
  4. 影响大数据和分析的5大趋势
  5. asp.net mvc 2.0 TryValidateModel(Object)方法
  6. System.Web.Mvc.UrlHelper的学习与使用
  7. 【Excel】按百分比随机抽取excel中数据
  8. 阿里云盘来袭,送几个福利码!手慢无!
  9. matlab 四面体体积
  10. Enzo Life Sciences/艾美捷丨线粒体/胞浆分离试剂盒
  11. Echarts地图实现点击某地区跳转到指定页面
  12. 我们常说祖宗十八代,到底是哪十八代?这个称呼又是怎么来的?
  13. 软件测试分类(按测试阶段划分)
  14. 堪比病毒营销的新思路:换个姿势玩抖音!
  15. linux命令vi编辑模式下小键盘不可用问题
  16. 贝乐机器人俱乐部_贝乐机器人编程俱乐部机器人课程介绍
  17. 【纯干货】你一定还不懂的,mysql缓存问题的解决方案
  18. shejimoshi设计模式
  19. Invalid query handle: xxxxxxxxxxxx
  20. oppok10pro和红米k50哪个值得买 两者配置对比

热门文章

  1. 英伟达再发边缘AI计算设备:仅信用卡大小,性能比TX2强15倍
  2. 什么是联机分析处理(OLAP)
  3. PathMatchingResourcePatternResolver通过适配符寻找符合条件的java类
  4. 从零开始学OpenDaylight(碳版本)之三:Hello示例
  5. Django--Uploaded Files以及Handlers
  6. MySQL AHI 实现解析
  7. 多线程——实现Callable接口
  8. MySQL中新建用户,新建数据库,用户授权,删除用户,修改密码的相关操作
  9. 华为手机有没有html,华为手机,到底有没有自己的核心技术?看内行人怎么说...
  10. python绘制动点_Python asyncore / asynchat 基本传输实验 - Jacky Liu's Blog