深度理解Vuex的用法及实例讲解

Vue
239
0
0
2024-04-16

背景:

在项目里面写一个公共的方法,只要有需要的table的column需要变色就直接调用这个方法就好了,不需要在自己的子页面写方法。 如下图,写一个column变色的公共方法。

在这里插入图片描述

源代码:

下面的源代码将以上面背景需求为例子进行展示 在Vuex/modules文件夹下面写:

action.js

import * as types from './types.js';

export default {
  TableHeightRun: ({ commit }, data) => {
    commit(types.TABLEHEIGHTRUN, data);
  },
  ViewSet_isFirst: ({ commit }, data) => {
    commit(types.ISFIRST, data);
  },
  setProductArr: ({ commit }, data) => {
    commit(types.COLORPRODUCT, data);
  },
};

getters.js

const View_TableHeight = (state) => {
  return state.View.TableHeight;
};
const View_UserInfo = (state) => {
  return state.View.UserInfo;
};
const View_UserInfor = (state) => {
  return state.View.UserInfor;
};
const ViewGet_isFirst = (state) => {
  return state.View.isFirst;
};
export default {
  View_TableHeight,
  View_UserInfor,
  View_UserInfo,
  ViewGet_isFirst,
}

mutations.js

import { TABLEHEIGHTRUN, USERINFO, USERINFOR, ISFIRST, COLORPRODUCT } from './types';
import Table from '../../../utils/tableHeight'; // 重新计算高度

export default {
  [TABLEHEIGHTRUN](state, data) {
    // 计算表格高度
    data = data || ['.find', '.paging', 135];
    state.View.TableHeight = Table(data);
  },
  [USERINFO](state, data) {
    // 存储用户的信息
    state.View.UserInfo = Object.assign(data);
  },
  [USERINFOR](state, data) {
    // 存储医疗机构用户的信息
    state.View.UserInfor = Object.assign(data);
  },
  [ISFIRST](state, data) {
    // 存储医疗机构用户的信息
    state.View.isFirst = data;
  },
  [COLORPRODUCT](state, data) {
    // 存储column颜色
    const greenArr = [];
    const yellowArr = [];
    const redArr = [];
    data.forEach((item) => {
      switch (item.colorType) {
        case 1:
          greenArr.push(item.drugsNo);
          break;
        case 2:
          yellowArr.push(item.drugsNo);
          break;
        case 3:
          redArr.push(item.drugsNo);
          break;
        default:
          break;
      }
    });
    state.View.styleJson.greenArr = greenArr;
    state.View.styleJson.yellowArr = yellowArr;
    state.View.styleJson.redArr = redArr;
  },
};

state.js

export default {
  View: {
    // 公共数据管理
    TableHeight: 0, // 表格高度计算
    UserInfo: {
      // 用户信息
      userId: '', // 用户id
      userName: '', // 用户姓名
      memberId: '', // 会员id
      memberNo: '', // 会员编号
      memberType: '', // 会员类型(1生产企业、2经营企业、3医疗机构、4gpo、5管理员)
    },
    UserInfor: {}, // 医疗机构用户信息,
    isFirst: true, // 是否第一次进入页面
    styleJson: {
      greenArr: [],
      yellowArr: [],
      redArr: [],
    },
  },
};

types.js

export const TABLEHEIGHTRUN = 'TABLEHEIGHTRUN'; // 重新计算表格高度
export const USERINFO = 'USERINFO'; // 获取用户的信息
export const USERINFOR = 'USERINFOR'; // 获取医疗机构用户的信息
export const ISFIRST = 'ISFIRST'; //  是否第一次进入系统
export const COLORPRODUCT = 'COLORPRODUCT'; //  颜色产品数组
export const TABLECELLSTYLE = 'TABLECELLSTYLE'; //  表格单元格样式

index.js

import getters from './getters'
import state from './state'
import actions from './actions'
import mutations from './mutations'

export default {
  mutations,
  getters,
  actions,
  state
}

cellStyleMixins.js(在mixins文件夹下面)

const baseColor = {
  green: 'rgba(164,227,149)',
  yellow: '#f7f494',
  red: '#fc97af',
};

import store from '@/vuex/store';

export default {
  data() {
    return {
      mKey: 'drugsNo',
      lKey: 'drugsName',
    };
  },
  methods: {
    tableCellStyleFn({ row, column }) {
      const { greenArr, yellowArr, redArr } = store.state.ViewActions.View.styleJson;
      const rowCode=row[this.mKey]||row.productNo||row.drugsCode;
      let item ={};
      if (greenArr.includes(rowCode) && column.property === this.lKey) {
        item = { backgroundColor: baseColor.green };
      }
      if (yellowArr.includes(rowCode) && column.property === this.lKey) {
        item = { backgroundColor: baseColor.yellow };
      }
      if (redArr.includes(rowCode) && column.property === this.lKey) {
        item = { backgroundColor: baseColor.red };
      }
      return item;
    },
  },
};

index.vue(需要引用方法的子页面)

 <el-table
  :cell-style="cellStyle"
  :data="retTable"
  :height="View_TableHeight"
  :summary-method="getSummaries"
  @selection-change="dataSelect"
  @sort-change="sortChange"
  border
  fit
  show-summary
  stripe
  >
 <el-table-column type="selection" width="40"></el-table-column>
 <el-table-column label="#" prop="num" show-overflow-tooltip width="40"></el-table-column>
 ... //此处省略多余的代码
 </el-table>

import cellStyleMixins from '@/mixins/cellStyleMixins';
export default {
  mixins: [cellStyleMixins],
  ...
   methods: {
    cellStyle({row, column, rowIndex, columnIndex }) {
      let colorRes = this.tableCellStyleFn({ row, column });
      if (row.redFlag === 1) {
        colorRes = {
          ...colorRes,
          color: 'red',
        }
      }
      return colorRes;
    },
   },
}

index2.vue

<template>
  <div>
    <DialogList
     :cellStyleClass="tableCellStyleFn"
      :isShowDialog="options.isShowDialog"
      :tableSelected="options.tableSelected"
      :tableType="1"
      :msgBase="options.msgBase"
      :options="optionsDialog"
      @pageChange="dialogPage"
      @dataSelect="dialogSelect"
      @dataSearch="dialogSearch"
      @dialogHide="dialogHide"
    >
    </DialogList>
  </div>
</template>

Vuex/store.js

import modules from './modules';
// 创建 store 实例
export default new Vuex.Store({
  modules,
});

假如你对上面的小例子代码看的有点云里雾里,没有关系,那么接下来我将带你详细的理解一遍Vuex是干嘛的,以及讲讲是如何使用的?话不多说,我们直接上干货!

Vuex干货来啦

vuex官方文档:https://vuex.vuejs.org/zh/

Vuex是什么

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

Vuex的原理

以下是一个表示“单向数据流”理念的简单示意:

在这里插入图片描述

但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

多个视图依赖于同一状态。 来自不同视图的行为需要变更同一状态。 对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。 对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!

通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。这就是 Vuex 背后的基本思想。

Vuex适用的场合

构建一个中大型单页应用,考虑如何更好地在组件外部管理状态,那么Vuex 是最好的选择。 简化之,就是应用简单的不用Vuex,一个简单的store模式就够了;要中大型单页应用以上才用它。

Vuex的组成部分及作用

state.js

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

// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,

    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}
getters.js

可以认为是 store 的计算属性。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。 注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。 mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:

mutations.js

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Mutation 必须是同步函数,在 mutation 中混合异步调用会导致你的程序很难调试,在 Vuex 中,mutation 都是同步事务。

action.js

Action 类似于 mutation,不同在于: Action 提交的是 mutation,而不是直接变更状态。 Action 可以包含任意异步操作。 Action 通过 store.dispatch 方法触发, mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store)。

module.js

模块(module) 为了解决"使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿"这个问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。

Vuex的项目结构

Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中。
  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  3. 异步逻辑都应该封装到 action 里面。

只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。

对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:

在这里插入图片描述