Vuex 的配置编写
目录
- Vuex 的配置编写
- Vuex 是什么?
- 什么是“状态管理模式”?
- 什么情况下我应该使用 Vuex?
- 安装 Vuex
- 开始使用 Vuex
- Action 文件
- Mutations-types 文件
- Mutation 文件
- Index
Vuex 是什么?
这里我们来看看官方网站是如何介绍 Vuex 的:
提示
这是与 Vue 3 匹配的 Vuex 4 的文档。如果您在找与 Vue 2 匹配的 Vuex 3 的文档,请在这里查看。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
什么是“状态管理模式”?
让我们从一个简单的 Vue 计数应用开始:
const Counter = {// 状态data () {return {count: 0}},// 视图template: `<div>{{ count }}</div>`,// 操作methods: {increment () {this.count++}}
}createApp(Counter).mount('#app')
这个状态自管理应用包含以下几个部分:
- 状态,驱动应用的数据源;
- 视图,以声明方式将状态映射到视图;
- 操作,响应在视图上的用户输入导致的状态变化。
以下是一个表示“单向数据流”理念的简单示意:
但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
- 多个视图依赖于同一状态。
- 来自不同视图的行为需要变更同一状态。
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!
通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。
这就是 Vuex 背后的基本思想,借鉴了 Flux、Redux 和 The Elm Architecture。与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。
如果你想交互式地学习 Vuex,可以看这个 Scrimba 上的 Vuex 课程,它将录屏和代码试验场混合在了一起,你可以随时暂停并尝试。
什么情况下我应该使用 Vuex?
Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。引用 Redux 的作者 Dan Abramov 的话说就是:
Flux 架构就像眼镜:您自会知道什么时候需要它。
安装 Vuex
我们可以使用下面的方式来安装 Vuex:
使用 npm 包管理器安装:
npm install vuex@next --save
使用 Yarn 包管理器安装:
yarn add vuex@next --save
在这里我们选择 npm 安装:
开始使用 Vuex
安装完成之后,就可以开始使用 vuex 了。我们先在 /src
中创建 /store
文件夹,并创建 js 文件 action.js
, index.js
, mutations-types.js
, mutations.js
。如下图所示:
新建目录结构如上图。从文件名可以看出其对应的作用,下面我们来编写对应的代码。
Action 文件
即图中的 action.js
文件。里面存放着各个 action,每个 action 都对应一个 mutation,下面是 action.js
文件的完整代码:
export default {// 展示下拉框showDropList({ commit }, data) {commit("SHOW_DROP_LIST", data);},// 更新编辑区内容updateContent({ commit }, data) {commit("UPDATE_CONTENT", data);},// 更新选择的值updateSelectValue({ commit }, data) {commit("UPDATE_SELECTED_VALUE", data);},// 更新菜单状态updateMenuStatus({ commit }, data) {commit("UPDATE_MENU_STATUS", data);},// 执行命令execCommand({ commit }, data) {commit("EXEC_COMMAND", data);},// 获取节点位置getNodePosition({ commit }, data) {commit("NODE_POSITION", data);},// 切换视图changeView({ commit }, data) {commit("CHANGE_VIEW", data);},
};
Mutations-types 文件
为了让整个项目的 mutation 可以很方便的查看和管理,我们将全部的 Mutation 事件类型的名字使用常量来代替,并将它们单独存放在一个文件中,对外提供导出接口。
mutations-types.js
代码内容如下:
export const SHOW_DROP_LIST = "SHOW_DROP_LIST";
export const UPDATE_CONTENT = "UPDATE_CONTENT";
export const UPDATE_SELECTED_VALUE = "UPDATE_SELECTED_VALUE";
export const UPDATE_MENU_STATUS = "UPDATE_MENU_STATUS";
export const EXEC_COMMAND = "EXEC_COMMAND";
export const CHANGE_VIEW = "CHANGE_VIEW";
export const NODE_POSITION = "NODE_POSITION";
在 action 中提交的 mutation 的名字,和这里的名字是保持一致的。
Mutation 文件
在这个文件中,定义了很多 mutation 用于改变 store 中的数据。在 action 中提交的 mutation 都将在这里真正生效。
首先,我们需要从mutations-types
中导入 mutation 的事件名称。这里使用对象结构的方式操作:
mutations.js
代码内容如下:
// 导入事件类型
import {// 下拉框事件类型SHOW_DROP_LIST,// 更新编辑区内容事件类型UPDATE_CONTENT,// 更新选择的值事件类型UPDATE_SELECTED_VALUE,// 更新菜单状态事件类型UPDATE_MENU_STATUS,// 执行命令事件类型EXEC_COMMAND,// 获取节点位置事件类型NODE_POSITION,// 切换视图事件类型CHANGE_VIEW,
} from "./mutations-types";// 定义处理各个事件类型的回调函数
export default {[SHOW_DROP_LIST]({ menuBar }, data) {// 下拉框事件类型对回调函数for (let menu in menuBar) {if (menuBar[menu].showDropList !== undefined) {if (data && data.name === menu) {menuBar[menu].showDropList = data.display;} else {menuBar[menu].showDropList = false;}}}},[UPDATE_CONTENT](state, data) {// 更新编辑区内容事件类型state.content = data;},[UPDATE_MENU_STATUS]({ menuBar }, data) {// 更新菜单状态事件类型if ("all" in data) {for (let menu in menuBar) {menuBar[menu].status = data.all;}return;}for (let name in data) {if (menuBar[name].showStatus) {menuBar[name].status = data[name];} else {menuBar[name].status = "default";}}},[UPDATE_SELECTED_VALUE]({ menuBar }, data) {// 更新选择的值事件类型menuBar[data.name].value = data.value;},[EXEC_COMMAND](state, data) {// 执行命令事件类型state.command = data;},[CHANGE_VIEW](state, data) {// 切换视图事件类型state.sourceView = data;},[NODE_POSITION](state, data) {// 获取节点位置事件类型state.position = {top: data.top,right: data.right,bottom: data.bottom + document.body.scrollTop,left: data.left,};},
};
Index
这个文件是保存数据的地方,状态数据的改变最终都会作用到里面的数据。 store 对象的实例也由这个文件导出。
index.js
的代码内容如下:
import { createStore } from "vuex";
// 导入一些依赖模块
import mutations from "./mutations";
import action from "./action";
import Menu from "@/config/menu";
import Config from "@/config/index";let menuBar = {}; // 菜单栏对象
let menu = Menu.getMenu(); // 获取全部菜单项配置信息
let config = Config.getConfig(); // 获取全部配置项
let viewMenu = config.viewMenu; // 获取可见的菜单项viewMenu.forEach(function (name) {menuBar[name] = {};// 是否有下拉框if (menu[name].dropList) {menuBar[name].value = "";menuBar[name].showDropList = false;} else {// 是否展示状态if (menu[name].showStatus) {menuBar[name].showStatus = true;}menuBar[name].status = "default";}
});export default createStore({state: {// 编辑区域内容content: config.container.content,// 菜单栏对象,包含菜单项状态数据menuBar,// 是否源码视图sourceView: false,//执行的命令command: {name: "",value: "",},//存放位置信息position: {top: 0,right: 0,bottom: 0,left: 0,},},getters: {},mutations: mutations,actions: action,modules: {},
});
以上所有操作完成之后,我们的 vuex 就配置完成了。不过目前还不能在组件中使用,因为还没将它注入到组件中去。在组件中使用 vuex 的方式有两种,一种是在每个单独的组件中都实例化一个 store 对象,然后对 store 实例对象进行操作。另外一种方式是通过在根组件处注入 store 实例,其他所有子组件可通过 this.$store
来访问到 store 实例对象,并进行操作。推荐使用第二种方式,更加简单方便。
我们修改一下入口文件 src/main.js
,注册 Vuex 实列 srore 对象:
import { createApp } from 'vue'
import App from './App.vue'
import store from './store';createApp(App).use(store).mount('#app')
ok,这样我们的项目就引入了 Vuex 了。