大型Vuex应用程序的目录结构
译者按: 听前端大佬聊聊Vuex大型项目架构的经验
为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。
在编写大型应用程序时,管理前端的状态可能会非常困难。 例如对于Vue.js应用程序,有一个名为Vuex的插件,它以非常简单的方式提供状态管理,并官方建议使用以下应用目录结构:

对示例很感兴趣,可以查看官方Vuex存储库(vuejs / vuex - shopping-cart)或者我创建的”购物车”示例(igeligel / vuex-simple-structure)。
官方推荐的目录结构是非常不错的,整个Vuex moudules看上去非常简单并且包含了moudules作用域中的actions,getters和mutations。共享actions,getters或者mutations被直接保存到store目录中。然后所有的modules,全局actions,getters和mutations被导入一个index.js文件中,并在Vuex module的构造函数中再次导出。然而,当你moudules越来越多的时候就会出现问题,对于大型项目来说这是普遍的。想一下像GitLab这样的大型项目,它包含非常非常多的modules。比如GitLab存储库视图的侧边栏看起来是这个样子的:

每个菜单条目基本上都是一个包含几个actions,getter和getters和mutations的module。 所有这些都列在单个module文件中。 这不会很好地扩展,因为考虑到module需要的功能越来越多,module就会变的非常大甚至会超过1000行代码。
但是这个问题有一个解决方案。 我们可以在module目录中提取actions,getters和mutations。 全局actions,getters或mutations可以直接存储在store目录中。 应用程序结构如下所示:

基本上,你仍然有可能使用全局actions,getter和mutations,但我并不推荐它,因为它并不是真的有必要。 采用这种方法,我们将拥有多个独立的文件。 聊天module的所有actions,getters和mutations将通过聊天目录内的索引导入。 这个module将被导入到全局store。 需要注意的是,你应该在模块内设置命名空间选项,以便你拥有适当的命名空间。 这是在store /index.js文件中完成的:
import Vue from 'vue'; |
在这个store里面,我们有两个module:chat和product。 两个模块都包含actions,getters和mutations,并被导入到module的主module文件index.js中,然后再次导出。 最后,导出的数据可以被store module使用。

这将注册modules,并且代码将以这样的方式分离,并且它仍然是可读的,可导航的和可维护的。 实现实例可查看bstavroulakis/vue-wordpress-pwa或者我自己实现的实例igeligel/vuex-namespaced-module-structure。 这个应用程序结构将很好地处理中小型应用程序。 代码库的新开发人员不会努力寻找业务逻辑所在的地方,因为每个模块都在组件内部有适当的名称和引用。 使用module真的很有趣,这在官方文档中有解释。
Fundebug错误实时监控为您的Vue项目保驾护航!
但是这样也有一个问题。当你后端团队创建越来越多的API时,并且程序变得越来越复杂,项目甚至达到20,30或者50个module。虽然维护没有问题,但是新加入的实习生可能就会因为架构纠结了,因为他不确定业务逻辑的调用方式。然后你就会想如何更好去架构。你可以直接在组件中执行API调用,但是这造成巨大混乱,因为组件将会持有业务逻辑。组件应该只呈现数据,不处理数据。
在React中有容器和组件的概念。 它没有被Vue.js强制执行。 容器只是组件,但它们也可以从store 获取数据并与store交互。 组件就在那里保存数据并渲染它。 他们通过道具与上层集装箱进行沟通。 让我们想象一下我们应用程序中的聊天窗口小部件,它需要从商店获取某种数据,或者从API获得更好的数据。 我们将通过从聊天中获取所有消息并且不提供实时支持来创建一个简单示例。 让我们假设我们有一些容器来保存整个聊天。 该容器将与商店通信以更新数据或将数据填充到表示组件。 整个架构在这个小图中显示:

在这个系统中,我们有一个名为Chat.vue的容器,它与我们的store module chat进行通信。 此chat module还通过调用API并更新store来处理逻辑。 当状态最终更新容器时,Chat.vue也将通过使用计算属性进行更新,计算属性将由Vue.js和Vuex的反应性更新。 之后,该属性将作为props(传值)传递给ChatList.vue。 由于props(传值)是该组件中的一个数组,所以会发生一次迭代,它将呈现一组ChatListElement.vue组件,它们负责呈现聊天消息和元信息。
有了这种模式,我们已将应用程序分为三部分。 一部分是业务逻辑,它存在于store的module内,或者更普遍地存储在store内,容器元素负责获取数据并将其填充到呈现组件,这些组件仅用于呈现数据。 这为我们提供了很好的模块化并支持单一责任原则。 它还提供了很好的可测试性,因为你可以自行测试此结构的每个部分。 他们一起将形成某种综合测试。 但是这可以在另一篇文章中讨论。
现在想象应用程序会增长很多。 很多的意思是指是你有几个modules,不清楚这些modules在哪里使用,哪些组件取决于它们,哪些不取决于它们。 在巨大的应用中,这可能是一个真正的问题。 想象一下,一个新的代码库可以忽略50个模块和大约50个组件。 他会有一个很大的问题来导航。
Vuex推荐是在store目录中有业务逻辑特征的目录。 有时与使用这些modules的容器的连接可能会断开,并且不清楚使用哪些Vuex modules的位置。 有些modules可能只是在那里,因为有一个容器,所以将这个业务逻辑放在处理数据的容器附近就好了。 让我们稍微调整应用程序。 该模板基于vuejs-templates/webpack。

唯一的区别是我将Vuex安装到这个模板中,设置它并在src目录下面添加modules目录。 稍后可以在此博客文章中找到此应用程序。 与此目录的区别在于它包含modules。 不要将这些modules与Vuex modules混淆。 有可能是一个更好的名字,所以如果你知道一个名字,请在这篇文章下评论它。 因此,在modules目录中,我们有这个Vue.js应用程序的模块。 它看起来像这样:

在modules目录中,有几个目录描述不同的功能。例如,我们有chat和product功能。但有趣的是,它们在这些module目录中,有一个store目录,一个index.vue文件和组件。清除一些干扰,我们只会看一下单个文件组件文件。 index.vue被用作容器组件。此容器将从store中提取所有数据,并将这些数据作为props(传值)传递给组件。组件ChatList.vue和ChatListElement.vue就是在那里从组件中获取数据并触发到store的行为,该store全局连接到Vue.js实例。最大的问题是为什么这些组件不在组件目录中。原因是这些组件是专门为此功能而制作的。如果他们将被重新用于其他功能,那么我会考虑将其移入组件目录。基本上这里的问题是,如果组件以某种方式被重用。然后我们应该将组件重构到共享组件目录中。现在来看store。它与其他模式基本相同,但移入本地目录store。要注册它,我们使用Vuex的registerModule函数。该功能将动态注册Vuex模块。通常它被用于插件,但我们将在这里使用它来更好地分离问题。在index.vue文件中,我们可以通过Vue.js访问生命周期函数,并且在创建的函数中,我们可以安全地创建module。
import { mapGetters } from 'vuex';
|
我们用$ _作为前缀,表明这个module是私有的,因为它只在module中可用。 注册后,该store将填充到我们的全局Vuex store。 从那里开始,我们可以在组件内使用这些Vuex功能。 要注册store,我们需要以某种方式将Vuex功能绑定到Vue.js实例。 这可以通过创建一个空的Vuex store,导出并将其附加到Vue.js构造函数来轻松完成。 查找这些文件以获取想法(store/index.js,main.js)。
如果我们发现自己需要某种全局store,我会在store目录下创建一个Vuex module,并使用推荐的结构。 例如,如果我们需要在应用程序内部的不同位置进行身份验证,最好以不与容器耦合的方式进行共享。 这对于拥有共享的Vuex module将是一个很好的实例。

一些问题:可能不清楚哪些module需要全局或本地可用,而且很难做出决定。 它也很难找到应该是全局的组件,但基本上,所有通用组件应该位于由不同module使用的目录中。 维持这种结构真的很难,但最终我认为为了扩展应用程序是值得的。 另一个问题是命名。 你现在有遍布整个地方的组件目录。 将模块_components中的目录命名为私有组件可能更好,但这是个人偏好。
这个结构的一个很好论点是modules在某种程度上是可以提取的。 如果一个功能变得太大,你可以通过在src/modules目录内的目录中创建一个module来提取它,然后制作一个npm软件包。 您需要导出的唯一东西是容器组件。 然后,这个npm包可以托管在公司的注册表中,也可以公布在npm上。 只要确保以某种方式使Vuex模块的行为可配置。 另一个很好的论点是测试可以用特征范围的方式编写。
最积极的论点是Vuex module的范围,容器和组件对于每个正在阅读代码的开发者都是清楚的。 由于在整个应用程序中使用关注点分离原则,因此可以快速找到每个功能的业务逻辑并且功能很容易测试。
不同结构的例子:
- igeligel/vuex-simple-structure
- igeligel/vuex-namespaced-module-structure
- igeligel/vuex-feature-scoped-structure
大型Vuex应用程序的目录结构的更多相关文章
- Android程序的目录结构
本篇文章我们将介绍Android应用程序的目录结构.本目录下有如图的目录结构: 下面我们来一 一介绍: 1. Src:该目录中存放的是该项目的源代码 2. Gen:该目录下的文件全部都 ...
- 3-微信小程序开发(小程序的目录结构说明)
https://www.cnblogs.com/yangfengwu/p/10050784.html 源码下载链接: 或者 这节先说一下小程序的目录结构 自行根据 https://www.cnblo ...
- 微信小程序基本目录结构学习
今天我们就以firstdemo为例,介绍一下小程序的基本目录结构.当我们打开一个微信小程序项目后,点击进入“编辑”菜单,我们可以看到有以下5个文件/文件夹):pages文件夹,utils文件夹,全局文 ...
- 微信小程序之目录结构
小程序,功能不会太多,页面不会太多. 正常情况下,会包含首页,分类页面,个人中心页面,导航页面,其他页面等等. 我们首先要把页面结构布置好,把架子搭建好. 剩下的就是配置一些内容,小程序的基本信息,接 ...
- 小程序的目录结构/配置介绍/视图层wxml数据绑定/双线程模型/小程序的启动流程
安装好微信小程序开发软件,创建项目 小程序文件结构和传统web对比 结构 传统web 微信小程序 结构 HTML WXML 样式 CSS WXSS 逻辑 Javascript Javascript 配 ...
- C++程序的目录结构、编译、打包、分发
管理C++的第三方库以及编译 第三方库这个说法,不知道出自哪里,但一般是指开发者,系统/平台提供商之外的第三个参与者提供的程序库. 大多数开源软件库在软件系统中都是第三方库. 完全不使用库的开发,在9 ...
- 微信小程序的目录结构解剖
在微信小程序当中,我们看到有: .js后缀文件,.json后缀文件,.wxss后缀文件,.wxml后缀文件 .js后缀文件就是我们写的普通的js文件 .json后缀文件就是小程序的配置文件,比如:wi ...
- 微信小程序购物商城系统开发系列-目录结构
上一篇我们简单介绍了一下微信小程序的IDE(微信小程序购物商城系统开发系列-工具篇),相信大家都已经蠢蠢欲试建立一个自己的小程序,去完成一个独立的商城网站. 先别着急我们一步步来,先尝试下写一个自己的 ...
- Android开发:程序目录结构详解
HelloWorld程序的目录结构概述 我们可以在文件夹中看到,HelloWorld程序的目录主要包括:src文件夹.gen文件夹.Android文件夹.assets.res文件夹. AndroidM ...
随机推荐
- UNIGUI与UNIURLFRAME的互动
UniSession.JSCode('name_'+MainForm.UniURLFrame1.name+'_'+MainForm.UniURLFrame1.JSName+'.myinput4.inn ...
- Centos7中docker开启远程访问
在作为docker远程服务的centos7机器中配置: 1.在/usr/lib/systemd/system/docker.service,配置远程访问.主要是在[Service]这个部分,加上下面两 ...
- Jira/Confluence的备份、恢复和迁移
之前的文章已经分别详细介绍了Jira.Confluence的安装及二者账号对接的操作方法,下面简单说下二者的备份.恢复和迁移: 一.Jira.Confluence的备份.恢复1)Confluence的 ...
- Javascript模式小记(一)
js总是可以在不知不觉中地创建了全局变量,其原因在于JavaScript的两个特性. 1.JavaScript可直接使用变量,甚至无需声明: 2.JavaScript有个暗示全局变量的概念,即任何变量 ...
- Swift5 语言指南(十二) 属性
属性将值与特定类,结构或枚举相关联.存储的属性将常量和变量值存储为实例的一部分,而计算属性则计算(而不是存储)值.计算属性由类,结构和枚举提供.存储的属性仅由类和结构提供. 存储和计算属性通常与特定类 ...
- OpenStack-Ocata版+CentOS7.6 云平台环境搭建 — 2.安装配置OpenStack基础服务
节点配置情况说明: 控制节点:controller: IP:192.168.164.128 hostname&hosts:likeadmin 计算加点:Nova: IP:192.168.164 ...
- 爬虫--反爬--css反爬---大众点评爬虫
大众点评爬虫分析,,大众点评 的爬虫价格利用css的矢量图偏移,进行加密 只要拦截了css 解析以后再写即可 # -*- coding: utf- -*- """ Cre ...
- java main()线程是不是最后一个退出的(相比较main中创建的其他多个线程)
JVM会在所有的非守护线程(用户线程)执行完毕后退出: main线程是用户线程: 仅有main线程一个用户线程执行完毕,不能决定JVM是否退出,也即是说main线程并不一定是最后一个退出的线程. pu ...
- 学习DDD之路--勇于纠正自己的错误
写这篇文章主要是之前三篇对DDD的介绍算是自己学习的一次试水,也希望能够有更多的人能帮我发现其中的问题.昨天继续阅读了DDD书,发现了自己之前的例子存在了一些问题,早上也和园友进行了一些讨论.最后整理 ...
- Linux编程 6 (查看进程 ps 及输出风格)
一.查看进程命令ps 1.1 默认ps 命令 在默认情况下,ps命令只会显示运行在当前控制台下,属于当前用户的进程,在上图中,我们只运行了bash shell以及ps命令本身. 上图中显示了程序的进程 ...