Vuex Quick Intro

Vuex

Vuex 概述

Vuex是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据共享

使用Vuex管理数据的好处: A.能够在vuex中集中管理共享的数据,便于开发和后期进行维护 B.能够高效的实现组件之间的数据共享,提高开发效率 C.存储在vuex中的数据是响应式的,当数据发生改变时,页面中的数据也会同步更新

Vuex 的基本使用

  1. 使用 quasar cli 创建一个 quasar 项目,并且使用当前目录

    quasar create .
    
  2. 在创建项目时候包括 vuex

    2020-09-21 16.03.29

  3. 在创建项目后配置 vscode.

    A. 打开 vscode 项目根目录下的 .vscode 目录下的 settings.json ,添加以下内容:

    {
      "editor.formatOnPaste": true,
      "editor.formatOnSave": true,
      "editor.codeActionsOnSave": {
        "source.fixAll": true
      },
      "javascript.format.insertSpaceBeforeFunctionParenthesis": true,
      "javascript.format.placeOpenBraceOnNewLineForControlBlocks": false,
      "javascript.format.placeOpenBraceOnNewLineForFunctions": false,
      "typescript.format.insertSpaceBeforeFunctionParenthesis": true,
      "typescript.format.placeOpenBraceOnNewLineForControlBlocks": false,
      "typescript.format.placeOpenBraceOnNewLineForFunctions": false,
      "vetur.format.defaultFormatter.html": "js-beautify-html",
      "vetur.format.defaultFormatter.js": "vscode-typescript"
    }
    

使用 Vuex 完成计数器案例

使用 Quasar 的 CLI 新建两个 Component: Add 和 Sub

quasarr new c Add
quasar new c Sub

之后更改 Add 与 Sub 的 templa 让他们都有一个标题

<template>
  <div>Add : 当前最新的 count 值为:</div>
</template>

<script>
export default {
  // name: 'ComponentName',
  data () {
    return {}
  }
}
</script>

更改 Layout 使得项目对计数器项目更加友好.

 Layout Builder
Visible Header
Visible Footer
Visible left-side Drawer
Visible right-side Drawer
Inject Drawer content for scrolling
Quasar
<template>
  <q-layout view="hHh lpR fFf">

    <q-header
      elevated
      class="bg-primary text-white"
    >
      <q-toolbar>
        <q-toolbar-title class="text-center">
          <q-avatar>
            <img src="https://cdn.quasar.dev/logo/svg/quasar-logo.svg">
          </q-avatar>
          Quasar 计数器 (使用 Vuex 实现)
        </q-toolbar-title>
      </q-toolbar>
    </q-header>

    <q-page-container>
      <router-view />
    </q-page-container>

    <q-footer
      elevated
      class="bg-grey-8 text-white"
    >
      <q-toolbar>
        <q-toolbar-title class="text-center">
          <q-avatar>
            <img src="https://cdn.quasar.dev/logo/svg/quasar-logo.svg">
          </q-avatar>
          AzatAI
        </q-toolbar-title>
      </q-toolbar>
    </q-footer>

  </q-layout>
</template>

<script>
export default {
  data () {
    return {
    }
  }
}
</script>

更改 index.vue 的值,实现项目的初步框架

<template>
  <q-page class="flex column content-center wrap  justify-center">
    <div>
      <a-add></a-add>
    </div>
    <div>
      <p class="q-mt-lg q-mb-lg">-----------------------------------------------</p>
    </div>
    <div>
      <a-sub></a-sub>
    </div>
  </q-page>
</template>

<script>
import Add from 'components/Add.vue'
import Sub from 'components/Sub.vue'
export default {
  name: 'PageIndex',
  data () {
    return {

    }
  },
  components: {
    'a-add': Add,
    'a-sub': Sub
  }
}
</script>

image-20200921164006855

Vuex 中的核心特性

State

创建一个 store 实例

State提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储

我们这里有一个统一数据,也就是 count 的值.因此我们可以在 store.js 中的 state 对象中定义这个数据(定义这个状态)

![State Management Vue.js](https://azatai.s3.amazonaws.com/static/2020-09-21-104341.png)
  1. 首先我们需要为我们的 counter 设置一个 store

    ❯ quasar new s counter
     App · Generated store: src/store/counter
     App · Make sure to reference it in src/store/index.js
    
  2. 然后我们将新创建的 counter 数据商店添加到我们的引用列表中

    import counter from './counter'
    
  3. 将 counter 这个 store 添加到我们的 store modules 当中去.

    const Store = new Vuex.Store({
        modules: {
          counter,
        },
    

完整的实例:

import Vue from "vue";
import Vuex from "vuex";

// import example from './module-example'
import counter from "./counter";
Vue.use(Vuex);

/*
 * If not building with SSR mode, you can
 * directly export the Store instantiation;
 *
 * The function below can be async too; either use
 * async/await or return a Promise which resolves
 * with the Store instance.
 */

export default function (/* { ssrContext } */) {
  const Store = new Vuex.Store({
    modules: {
      counter,
    },

    // enable strict mode (adds overhead!)
    // for dev mode only
    strict: process.env.DEV,
  });

  return Store;
}

在 Quasar 项目中使用 store 中访问 state

首先在我们的全局 state 对象中添加一些变量

export default function () {
  return {
    counter: 0,
  };
}

在组件中访问 state 有两种方式:

1. 通过this.$store.state.MODULE_NAME.VAR_NAME 的方式使用 state 中的数据.

例如我们在 Add 这个组件中通过 this.$store.state.counter.counter 的方式访问到了数据并展示在页面中:

<template>
  <div>
    Add : 当前最新的 count 值为: 
  </div>
</template>

<script>
export default {
  // name: 'ComponentName',
  data() {
    return {};
  },
};
</script>

image-20200921180941151

在 template 区域中 this 关键词是可以省略的,所以上述代码也可以写成:

<template>
  <div>Add : 当前最新的 count 值为: </div>
</template>

<script>
export default {
  // name: 'ComponentName',
  data() {
    return {};
  },
};
</script>

并不会影响我们的结果.

2. 通过 mapStore 函数访问 state 中的数据

我们可以通过 mapStore 函数访问store 中的数据.

首先我们需要导入 mapState 函数

import { mapState } from "vuex";

然后将state 中的变量映射为当前组件的一个 computed 属性

  computed: {
    ...mapState("counter", ["counter"]),
  },

然后在组件中像调用我们的一个 computed 数据一样调用我们的数据.

<template>
  <div>Sub : 当前最新的 count 值为: </div>
</template>

注意: 上述方式,我们先获取 counter 这个数据模型,然后再访问模型中的数据变量中数据.

完整的实例代码:

<template>
  <div>Sub : 当前最新的 count 值为: </div>
</template>

<script>
import { mapState } from "vuex";
export default {
  // name: 'ComponentName',
  data() {
    return {};
  },
   computed: {
    ...mapState("counter", ["counter"]),
  },
};
</script>

image-20200921181934839

Mutation

Mutation用于修改变更$store中的数据

创建 Mutation

我们编辑了 store/counter/mutations.js 文件并添加了以下内容:

export function add(state) {
  state.counter += 1;
}
export function addN(state, N) {
  state.counter += N;
}

注意:

  1. 第一个形参永远都是state也就是$state对象
  2. 第二个形参是调用add时传递的参数

使用 mutation

同样有两种方法使用 mutation

1. 在组件的 methods 通过 this 对象调用 mutation 函数 (commit)
methods: {
    Add() {
      this.$store.commit("counter/add");
    },
  },

然后在组件 template 中触发这个函数.

<q-btn color="primary" label="增加 1" class="q-mb-lg" @click="Add" />

效果:

2020-09-21 18.33.17

同样地 编写 减少 1 的函数.

export function add(state) {
  state.counter += 1;
}
export function addN(state, N) {
  state.counter += N;
}
export function sub(state) {
  state.counter -= 1;
}
export function subN(state, N) {
  state.counter -= N;
}
<template>
  <div>
    <q-btn color="primary" label=" 减少 1" class="q-mb-lg" @click="Sub" />
    <div>Sub : 当前最新的 count 值为: </div>
  </div>
</template>

<script>
import { mapState } from "vuex";
export default {
  // name: 'ComponentName',
  data() {
    return {};
  },
    computed: {
    ...mapState("counter", ["counter"]),
  },
  methods: {
    Sub() {
      this.$store.commit("counter/sub");
    },
  },
};
</script>

image-20200921183602290

2. 通过 mapMutations 将 mutation 中的函数映射为项目组件的 methods 中函数

首先按需导入我们的 mapMutations 函数.

import { mapState, mapMutations } from "vuex";

在 methods 中映射函数:

 methods: {
    //...mapMutations('counter', ['sub'])
    // ...mapMutations('common', ['func1', 'func2', 'func3'])
    ...mapMutations("counter", ["sub"]),
  },

一定要注意格式 !! 若要从不同的 model 中导入,则需要另起一行 1!

完整代码:

Add.vue

<template>
  <div>
    <q-btn color="primary" label="增加 1" class="q-mb-lg" @click="Add" />
    <q-btn
      color="primary"
      label="增加 N"
      class="q-mb-lg q-ml-sm"
      @click="AddN"
    />
    <div>Add : 当前最新的 count 值为: </div>
  </div>
</template>

<script>
export default {
  // name: 'ComponentName',
  data() {
    return {};
  },
  methods: {
    Add() {
      this.$store.commit("counter/add");
    },
    AddN() {
      this.$store.commit("counter/addN", 3);
    },
  },
};
</script>

Sub.vue

<template>
  <div>
    <q-btn color="primary" label=" 减少 1" class="q-mb-lg" @click="sub" />
    <q-btn
      color="primary"
      label=" 减少 1"
      class="q-mb-lg q-ml-sm"
      @click="subN(3)"
    />
    <div>Sub : 当前最新的 count 值为: </div>
  </div>
</template>

<script>
import counter from "src/store/counter";
import { mapState, mapMutations } from "vuex";
export default {
  // name: 'ComponentName',
  data() {
    return {};
  },
  computed: {
    ...mapState("counter", ["counter"]),
  },
  methods: {
    //...mapMutations('counter', ['sub'])
    // ...mapMutations('common', ['func1', 'func2', 'func3'])
    ...mapMutations("counter", ["sub", "subN"]),
  },
};
</script>

效果:

2020-09-21 19.32.34

Action

在mutations中不能编写异步的代码,会导致vue调试器的显示出错。

在vuex中我们可以使用Action来执行异步操作。

这里我们通过延时器来做一个异步的演示

创建 Action

在quasar 的store 中的 counter model 中的 actions.js 我们创建了以下几个action:

export function addAsync(context) {
  setTimeout(function () {
    context.commit("add");
  }, 1000);
}
export function addAsyncN(context, N) {
  setTimeout(function () {
    context.commit("addN", N);
  }, 1000);
}
export function subAsync(context) {
  setTimeout(function () {
    context.commit("sub");
  }, 1000);
}
export function subAsyncN(context, N) {
  setTimeout(function () {
    context.commit("subN", N);
  }, 1000);
}

之后我们分别在Add。vue 以及在 Sub.Vue 中创建预设的安妮.

<template>
  <div>
    <q-btn color="primary" label="增加 1" class="q-mb-lg" @click="Add" />
    <q-btn
      color="primary"
      label="增加 N"
      class="q-mb-lg q-ml-sm"
      @click="AddN"
    />
    <q-btn
      color="primary"
      label="异步增加 1"
      class="q-mb-lg q-ml-sm"
      @click="AddN"
    />
    <q-btn
      color="primary"
      label=" 异步增加 N"
      class="q-mb-lg q-ml-sm"
      @click="AddN"
    />
    <div>Add : 当前最新的 count 值为: </div>
  </div>
</template>

<script>
export default {
  // name: 'ComponentName',
  data() {
    return {};
  },
  methods: {
    Add() {
      this.$store.commit("counter/add");
    },
    AddN() {
      this.$store.commit("counter/addN", 3);
    },
  },
};
</script>

使用 Action

通过 dispatch 使用

以下是 Add.vue

<template>
  <div>
    <q-btn color="primary" label="增加 1" class="q-mb-lg" @click="Add" />
    <q-btn
      color="primary"
      label="增加 N"
      class="q-mb-lg q-ml-sm"
      @click="AddN"
    />
    <q-btn
      color="primary"
      label="异步增加 1"
      class="q-mb-lg q-ml-sm"
      @click="AddAsync"
    />
    <q-btn
      color="primary"
      label=" 异步增加 N"
      class="q-mb-lg q-ml-sm"
      @click="AddAsyncN"
    />
    <div>Add : 当前最新的 count 值为: </div>
  </div>
</template>

<script>
export default {
  // name: 'ComponentName',
  data() {
    return {};
  },
  methods: {
    Add() {
      this.$store.commit("counter/add");
    },
    AddN() {
      this.$store.commit("counter/addN", 3);
    },
    AddAsync() {
      this.$store.dispatch("counter/addAsync");
    },
    AddAsyncN() {
      this.$store.dispatch("counter/addAsyncN", 3);
    },
  },
};
</script>

效果:

2020-09-21 20.46.55

通过 mapActions 映射的方式使用

首选我们需要导入:

import { mapState, mapMutations, mapActions } from "vuex";

之后再 methods 中映射:

<template>
  <div>
    <q-btn color="primary" label=" 减少 1" class="q-mb-lg" @click="sub" />
    <q-btn
      color="primary"
      label=" 减少 N"
      class="q-mb-lg q-ml-sm"
      @click="subN(3)"
    />
    <q-btn
      color="primary"
      label=" 异步减少 1"
      class="q-mb-lg q-ml-sm"
      @click="subAsync"
    />
    <q-btn
      color="primary"
      label=" 异步减少 N"
      class="q-mb-lg q-ml-sm"
      @click="subAsyncN(5)"
    />
    <div>Sub : 当前最新的 count 值为: </div>
  </div>
</template>

<script>
import counter from "src/store/counter";
import { mapState, mapMutations, mapActions } from "vuex";
export default {
  // name: 'ComponentName',
  data() {
    return {};
  },
  computed: {
    ...mapState("counter", ["counter"]),
  },
  methods: {
    //...mapMutations('counter', ['sub'])
    // ...mapMutations('common', ['func1', 'func2', 'func3'])
    ...mapMutations("counter", ["sub", "subN"]),
    // Actions
    ...mapActions("counter", ["subAsync", "subAsyncN"]),
  },
};
</script>

效果:

2020-09-21 20.51.01

Getter

Getter用于对Store中的数据进行加工处理形成新的数据. Store 中的 getter 其实就是像 Vue 中的 computed 属性,没有什么不一样的.

它只会包装Store中保存的数据,并不会修改Store中保存的数据,当Store中的数据发生变化时,Getter生成的内容也会随之变化.

创建 Getter

// /*
export function counterText(state) {
  return "最新的 counter 的数值为: " + state.counter;
}
// */

使用 Getter

通过 mapGetters 映射到 computed 中使用

先导入 mapGetters

import { mapState, mapMutations, mapActions, mapGetters } from "vuex";

然后将其映射到 computed 属性中:

<template>
  <div>
    <q-btn color="primary" label=" 减少 1" class="q-mb-lg" @click="sub" />
    <q-btn
      color="primary"
      label=" 减少 N"
      class="q-mb-lg q-ml-sm"
      @click="subN(3)"
    />
    <q-btn
      color="primary"
      label=" 异步减少 1"
      class="q-mb-lg q-ml-sm"
      @click="subAsync"
    />
    <q-btn
      color="primary"
      label=" 异步减少 N"
      class="q-mb-lg q-ml-sm"
      @click="subAsyncN(5)"
    />
    <div>Sub : 当前最新的 count 值为: </div>
    <div></div>
  </div>
</template>

<script>
import counter from "src/store/counter";
import { mapState, mapMutations, mapActions, mapGetters } from "vuex";
export default {
  // name: 'ComponentName',
  data() {
    return {};
  },
  computed: {
    // state
    ...mapState("counter", ["counter"]),
    // Getters
    ...mapGetters("counter", ["counterText"]),
  },
  methods: {
    //...mapMutations('counter', ['sub'])
    // ...mapMutations('common', ['func1', 'func2', 'func3'])
    ...mapMutations("counter", ["sub", "subN"]),
    // Actions
    ...mapActions("counter", ["subAsync", "subAsyncN"]),
  },
};
</script>

效果:

2020-09-21 21.02.26