Vuex 概述
Vuex是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据共享
使用Vuex管理数据的好处: A.能够在vuex中集中管理共享的数据,便于开发和后期进行维护 B.能够高效的实现组件之间的数据共享,提高开发效率 C.存储在vuex中的数据是响应式的,当数据发生改变时,页面中的数据也会同步更新
Vuex 的基本使用
-
使用 quasar cli 创建一个 quasar 项目,并且使用当前目录
quasar create .
-
在创建项目时候包括 vuex
-
在创建项目后配置 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>
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) |
-
首先我们需要为我们的 counter 设置一个 store
❯ quasar new s counter App · Generated store: src/store/counter App · Make sure to reference it in src/store/index.js
-
然后我们将新创建的 counter 数据商店添加到我们的引用列表中
import counter from './counter'
-
将 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>
在 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>
Mutation
Mutation用于修改变更$store中的数据
创建 Mutation
我们编辑了 store/counter/mutations.js 文件并添加了以下内容:
export function add(state) {
state.counter += 1;
}
export function addN(state, N) {
state.counter += N;
}
注意:
- 第一个形参永远都是state也就是$state对象
- 第二个形参是调用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" />
效果:
同样地 编写 减少 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>
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>
效果:
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>
效果:
通过 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>
效果:
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>
效果: