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

  状态?我把它理解为在data中的属性需要共享给其他vue组件使用的部分,就叫做状态。简单的说就是data中需要共用的属性。

  使用Vue开发项目时,通常我们就会遇到如下几种棘手的问题:

  问题1:通过路由传递参数,我们会采用params或者query形式,但这两种方式都会在URL上做手脚,如果传递的参数过多,会导致400 Bad Request(如:点击表格某行,携带行数据跳转到新页面进行查看)。

  问题2:兄弟组件传值

  问题3:多处地方使用同一数据,为节省重复请求(最为常见)

  业务场景:

  从A页面携带下钻参数(ID)到B页面,然后B页面获取参数(ID)进行数据请求。由于要下钻的ID过长,受浏览器的URL长度限制问题,我们不能采用常规的prams(xxx.com/B/:ID)或query(xxx.com/B?ID=…)形式传递。可以使用Vuex做中间过渡,跳转前存储ID信息,进入B页面后从Vuex获取ID信息。

  如果用户在B页面刷新数据,则Vuex的ID状态值会被清空无法获取,这里只能借助localStorage进行持久化进行处理(当然,如果直接使用localstorage进行持久化存储,而不借助Vuex也是可行的,但就是没了响应式的效果了)

一、常见使用问题

1、Vuex,每次调用mutation之后向localstorage存值,防止刷新丢失

  1. export default {
  2. state:{
  3. reportInfo:null
  4. },
  5. getters:{
  6. reportInfo(state){
  7. if(!state.reportInfo){
  8. state.reportInfo = JSON.parse(sessionStorage.getItem('reportInfo'))
  9. }
  10. return state.reportInfo
  11. }
  12. },
  13. mutations:{
  14. initReport:(state,data) => {
  15. state.reportInfo = data
  16. sessionStorage.setItem('reportInfo',JSON.stringify(data))
  17. },
  18. changeReportReview:(state,data) => {
  19. state.reportInfo.is_review = data;//改state的reportInfo里的某个值,
    同时修改sessionStorage的值,以保证一样
  20. sessionStorage.setItem('reportInfo',JSON.stringify(state.reportInfo))
  21. }
  22. }
  23. }

2、调用时需要追加模块名称

  如上的getters,首先从store中获取,如果store中不存在则从localstorage中获取(刷新)

3、页面中如果有用户登出操作,此时一般需要removeItem,如果页面中有userInfo的判断信息,如下一般需要先做个判断userInfo存在才会去取userInfo.roleName的值,这样就会防止页面报错,类似于后台语言空指针的错误

  1. <span class="el-dropdown-link">
  2. {{userInfo ? (userInfo.account ? userInfo.account : userInfo.phoneNum) : ""}}
  3. <i class="iconfont icon-user"></i>
  4. </span>
  1. <router-link :to="'/account'" v-if="userInfo && userInfo.roleName === 'sys'">
  2. <el-dropdown-item>添加账户</el-dropdown-item>
  3. </router-link>

  如果没有removeItem的操作的话,则是不需要这样的。如下面的reportInfo,因为用户登出的时候,不会影响到它

  1. <div class="suggess_requert" v-if="suggess.suggestion_level === 3">
  2. <div v-if="reportInfo.is_review === 1">
  3. <router-link v-if="userInfo && userInfo.roleName === 'dba'" :to="'/list/review'">
  4. <h3>提交审核意见</h3>
  5. </router-link>
  6. <h3 v-else>专家正在审核,请稍后</h3>
  7. </div>
  8. <router-link v-else-if="reportInfo.is_review === 2" :to="'/list/reviewInfo/' + reportInfo.report_id">
  9. <h3>查看云和恩墨专家团队审核意见</h3>
  10. </router-link>
  11. <div v-else>
  12. <h3 v-if="userInfo && userInfo.roleName === 'user'" @click="reviewRequest()">一键请求云和恩墨专家团队帮您二次审核</h3>
  13. </div>
  14. </div>

4、state访问状态对象

  const state ,这个就是我们说的访问状态对象,它就是我们SPA(单页应用程序)中的共享值。

  学习状态对象赋值给内部对象,也就是把stroe.js中的值,赋值给我们模板里data中的值。有三种赋值方式:

(1)通过computed的计算属性直接赋值

  computed属性可以在输出前,对data中的值进行改变,我们就利用这种特性把store.js中的state值赋值给我们模板中的data值。

  1. computed:{
  2. count(){
  3. return this.$store.state.count;
  4. }
  5. }

  这里需要注意的是return this.$store.state.count这一句,一定要写this,要不你会找不到$store的。这种写法很好理解,但是写起来是比较麻烦的,那我们来看看第二种写法。

(2)通过mapState的对象来赋值

  我们首先要用import引入mapState。

  1. import {mapState} from 'vuex';
  2.  
  3. //然后还在computed计算属性里写如下代码:
  4. computed:mapState({
  5. count:state=>state.count //理解为传入state对象,修改为state.count属性
  6. })

  这里我们使用ES6的箭头函数来给count赋值。

(3)通过mapState的数组来赋值

  1. computed:mapState(["count"])

  这个算是最简单的写法了,在实际项目开发当中也经常这样使用

5、模板获取Mutations方法

  实际开发中我们也不喜欢看到$store.commit( )这样的方法出现,我们希望跟调用模板里的方法一样调用。 例如:@click=”reduce” 就和没引用vuex插件一样。要达到这种写法,只需要简单的两部就可以了:

  1. //1、用import 引入我们的mapMutations:
  2. import {mapGetters,mapMutations} from 'vuex'
  3. //2、添加methods属性,并加入mapMutations
  4. ...mapMutations(['changeReportReview']),

  mapGetters、mapActions,都是一样的用法

  1. methods:{
  2. ...mapMutations([
  3. 'add','reduce'
  4. ]),
  5. ...mapActions(['addAction','reduceAction'])
  6. },

  需要注意的是,(1)调用的时候都是需要加上this才能访问到的;(2)getters/mutations/actions这些如果没带命名空间的话,就算是写在module模块里的这些方法,但是在使用的时候,是不需要加上模块名称的,而state是要加上模块名称的

二、状态管理模式

  “单向数据流”理念的极简示意:

  • state,驱动应用的数据源;
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入导致的状态变化。

  我们在开发中会遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏。

  • 多个视图依赖于同一状态。
  • 来自不同视图的行为需要变更同一状态。

  对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件(非父子组件)间的状态传递无能为力;

  对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

三、Vuex简介

  Vuex 和单纯的全局对象有以下两点不同:

  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  • 不能直接改变 store 中的状态改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
  1. Vue.use(Vuex);
  2. const store = new Vuex.Store({
  3. // 数据状态
  4. state {...},
  5. // 更改状态 store.commit
  6. mutations: {...},
  7. // 类似于mutation(不能直接变更状态,可以异步操作) store.dispatch
  8. actions: {...},
  9. // 派生状态(如,过滤、计数)
  10. getters: {...}
  11. })
  12.  
  13. // 将状态从根组件“注入”到每一个子组件中,且子组件能通过 this.$store 访问到。
  14. const app = new Vue({
  15. el: '#app',
  16. store,
  17. data() {}
  18. });

四、State

  Vuex 使用单一状态树,这可以让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。需要注意,单状态树和模块化并不冲突!

  由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。

  1. computed: {
  2. count () {
  3. return store.state.count // this.$store.state.count
  4. }
  5. }

  mapState 辅助函数

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

  1. import { mapState } from 'vuex'
  2.  
  3. computed: mapState({
  4. // 映射 this.count 为 store.state.count
  5. 'count',
  6.  
  7. // 箭头函数可使代码更简练
  8. count: state => state.count,
  9.  
  10. // 传字符串参数 'count' 等同于 `state => state.count`
  11. countAlias: 'count',
  12.  
  13. // 为了能够使用 `this` 获取局部状态,必须使用常规函数
  14. countPlusLocalState (state) {
  15. return state.count + this.localCount
  16. },
  17.  
  18. // 使用对象展开运算符将此对象混入到外部对象中
  19. ...mapState({
  20. // ...
  21. })
  22. })

五、Getter

  Getter(state, getters)可以从 store 中的 state 中派生出一些状态(如,对数据进行过滤操作)。对于多个组件需要用同一属性时,意义重大!类似于计算属性,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

  完整请参照 https://vuex.vuejs.org/zh-cn/getters.html

六、Mutation

  mutation 必须是同步函数!!!

  更改 Vuex 的 store 中的状态的唯一方法是提交 mutation

  Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。调用 store.commit(type, payload) 方法来触发mutations中的相关方法。

  1. mutations: {
  2. increment (state, n) {
  3. state.count += n
  4. }
  5. }
  6.  
  7. store.commit('increment', )

  注意:Mutation 需遵守 Vue 的响应规则

  • 最好提前在你的 store 中初始化好所有所需属性

  • 当需要在对象上添加新属性时,你应该

    • 使用 Vue.set(obj, 'newProp', 123), 或者

    • 以新对象替换老对象。例如,利用 stage-3 的对象展开运算符我们可以这样写

  1. state.obj = { ...state.obj, newProp: }

  完整请参照:https://vuex.vuejs.org/zh-cn/mutations.html

七、Action

  Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。
  • 通过 store.dispatch 方法触发

  组合 Action:store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise。

  1. actions: {
  2. actionA ({ commit }) {
  3. return new Promise((resolve, reject) => {
  4. setTimeout(() => {
  5. commit('someMutation')
  6. resolve()
  7. }, )
  8. })
  9. }
  10. }
  11. //现在你可以:
  12. store.dispatch('actionA').then(() => {
  13. // ...
  14. })

  在另外一个 action 中也可以:

  1. // 假设 getData() 和 getOtherData() 返回的是 Promise
  2. actions: {
  3. async actionA ({ commit }) {
  4. commit('gotData', await getData())
  5. },
  6. async actionB ({ dispatch, commit }) {
  7. await dispatch('actionA') // 等待 actionA 完成
  8. commit('gotOtherData', await getOtherData())
  9. }
  10. }

  一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。

  完整请参照:https://vuex.vuejs.org/zh-cn/actions.html

八、Module

  由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)

  默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为命名空间模块当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

九、插件

  Vuex 的 store 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函数,它接收 store 作为唯一参数。

  1. const myPlugin = store => {
  2. // 当 store 初始化后调用
  3. store.subscribe((mutation, state) => {
  4. // 每次 mutation 之后调用
  5. // mutation 的格式为 { type, payload }
  6. })
  7. }

  然后像这样使用:

  1. const store = new Vuex.Store({
  2. // ...
  3. plugins: [myPlugin]
  4. })

  项目中我们会使用plugin来初始化一些数据

  1. const initActionList = [
  2. 'base/' + INIT_BUSINESS_SYSTEM_LIST,
  3. 'threat/' + INIT_STD_COEFFICIENT_LIST
  4. ]
  5. export default function (store) {
  6. for (let action of initActionList) {
  7. Bus.$once(action, () => {
  8. store.dispatch(action)
  9. })
  10. }
  11. }

  但是 ,使用plugin的Bus.$once去初始化请求,而不再每个使用模块自身dispatch。会有解决不掉的两个问题:

  • 点击某个按钮触发相关数据($once只适合初始化时请求)
  • 某请求依赖store中的情况(刷新)await dispatch('actionA') // 等待 actionA 完成

十、表单处理

  当在严格模式中使用 Vuex 时,在属于 Vuex 的 state 上使用 v-model 会比较棘手

  1. <input v-model="obj.message">

  在用户输入时,v-model 会试图直接修改 obj.message。在严格模式中,由于这个修改不是在 mutation 函数中执行的, 这里会抛出一个错误。使用传统的value+input事件实现,但是比较啰嗦。

  1. <input :value="message" @input="updateMessage">
  2.  
  3. computed: {
  4. ...mapState({
  5. message: state => state.obj.message
  6. })
  7. },
  8. methods: {
  9. updateMessage (e) {
  10. this.$store.commit('updateMessage', e.target.value)
  11. }
  12. }

  这里,可以使用双向绑定的计算属性

  1. computed: {
  2. message: {
  3. get () {
  4. return this.$store.state.obj.message
  5. },
  6. set (value) {
  7. this.$store.commit('updateMessage', value)
  8. }
  9. }
  10. }

  总结:使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。

详解Vuex常见问题、深入理解Vuex的更多相关文章

  1. Java虚拟机详解----JVM常见问题总结

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  2. Mysql加锁过程详解(8)-理解innodb的锁(record,gap,Next-Key lock)

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  3. Android菜单详解(一)——理解android中的Menu

    前言 今天看了pro android 3中menu这一章,对Android的整个menu体系有了进一步的了解,故整理下笔记与大家分享. PS:强烈推荐<Pro Android 3>,是我至 ...

  4. WmS详解(二)之如何理解Window和窗口的关系?基于Android7.0源码

    上篇博客(WmS详解(一)之token到底是什么?基于Android7.0源码)中我们简要介绍了token的作用,这里涉及到的概念非常多,其中出现频率最高的要数Window和窗口这一对搭档了,那么我们 ...

  5. JNI详解---从不懂到理解

    转载:https://blog.csdn.net/hui12581/article/details/44832651 Chap1:JNI完全手册... 3 Chap2:JNI-百度百科... 11 C ...

  6. 百度大脑UNIT3.0详解之嵌入式对话理解技术

    相信很多人都体验过手机没有网时的焦虑,没有网什么也做不了.而机器人也会遇到这样的时刻,没有网或者网络环境不好的情况下,无法识别用户在说什么,也无法回复用户.在AIoT(AI+物联网)飞速普及的现在,智 ...

  7. Nginx gzip参数详解及常见问题(已解决)

    1.Nginx gzip功能 Nginx实现资源压缩的原理是通过ngx_http_gzip_module模块拦截请求,并对需要做gzip的类型做gzip,ngx_http_gzip_module是Ng ...

  8. 超详细的HDFS读写流程详解(最容易理解的方式)

    HDFS采用的是master/slaves这种主从的结构模型管理数据,这种结构模型主要由四个部分组成,分别是Client(客户端).Namenode(名称节点).Datanode(数据节点)和Seco ...

  9. java 基本类型详解 及 常见问题

    鄙人不才,基础不好,趁着闲时简单学习一下,仅作学习分享,如有不正确地方还请各位看客不吝指出. 常用的基本类型有:byte(8).short(16).char(16,取值从0-65535[2^16-1] ...

  10. 详解基于vue,vue-router, vuex以及addRoutes进行权限控制

    基于vuex, vue-router,vuex的权限控制教程,完整代码地址见https://github.com/linrunzheng/vue-permission-control 接下来让我们模拟 ...

随机推荐

  1. Https 请求工具(put,post,get)

    package com.util; /** * @Description: 类描述 * @author 作者 ll E-mail:80002132@sf-express.com * @version ...

  2. C#中泛型的使用

    1. List<T> 2. Dictionary<TKey, TValue> 命名空间:using System.Collections.Generic; 普通数组:在声明时必 ...

  3. 洛谷P2464 [SDOI2008] 郁闷的小j [分块]

    题目传送门 郁闷的小j 题目描述 小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架.虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他面临着被解雇的危险,这也 ...

  4. C++ string to char *

    半伪代码: void main { string line; getline(ifs, line); replace(line.begin(), line.end(), ',', '\n'); rep ...

  5. `__pycache__` 是什么

    为了提高模块加载的速度,每个模块都会在 __pycache__ 文件夹中放置该模块的预编译模块,命名为 module.version.pyc, version 是模块的预编译版本编码,一般都包含 Py ...

  6. freemarker${}包含${}

    ${}包含${} freemarker还是比较只能的,只是你自己复杂化了 比如有两个集合 books跟users 你可以这么取值吗,索引是有关联关系的 <#list users as user& ...

  7. poj1789(prim)

    prim和kruskal都是求解最小生成树的算法.这道题题意就是有N个字符串就是N个节点,而字符串之间的距离就是节点边的长度,求其最小生成树的边权和. 由于是第一次用prim,所以在求安全边的时候采用 ...

  8. iOS 9音频应用播放音频之音量设置与声道设置

    iOS 9音频应用播放音频之音量设置与声道设置 iOS 9音频应用音量设置 音量又称响度.音强,是指人耳对所听到的声音大小强弱的主观感受,其客观评价尺度是声音的振幅大小.在iOS 9音频应用的应用中, ...

  9. java8新特性——简介

    java8问世已经有好长时间了,但是之前项目中都没有使用到,所以一直都只是了解一些,近期刚刚换了家新公司,在开发中需要使用到java8来开发,所以也是马上赶来学习一下java8得新特性. 一.新特性 ...

  10. 探究react-native 源码的图片缓存

    先看js端图片使用的三种方式,依次排序1.2.3 <Image source={{uri:url}} style={{width:200,height:200}}/> 1. 加载远程图片 ...