译者按: 听前端大佬聊聊Vuex大型项目架构的经验html
为了保证可读性,本文采用意译而非直译。另外,本文版权归原做者全部,翻译仅用于学习。前端
在编写大型应用程序时,管理前端的状态可能会很是困难。 例如对于Vue.js应用程序,有一个名为Vuex的插件,它以很是简单的方式提供状态管理,并官方建议使用如下应用目录结构:vue
对示例很感兴趣,能够查看官方Vuex存储库(vuejs / vuex - shopping-cart)或者我建立的”购物车”示例(igeligel / vuex-simple-structure)。webpack
官方推荐的目录结构是很是不错的,整个Vuex moudules看上去很是简单而且包含了moudules做用域中的actions,getters和mutations。共享actions,getters或者mutations被直接保存到store目录中。而后全部的modules,全局actions,getters和mutations被导入一个index.js
文件中,并在Vuex module的构造函数中再次导出。然而,当你moudules愈来愈多的时候就会出现问题,对于大型项目来讲这是广泛的。想一下像GitLab这样的大型项目,它包含很是很是多的modules。好比GitLab存储库视图的侧边栏看起来是这个样子的:git
每一个菜单条目基本上都是一个包含几个actions,getter和getters和mutations的module。 全部这些都列在单个module文件中。 这不会很好地扩展,由于考虑到module须要的功能愈来愈多,module就会变的很是大甚至会超过1000行代码。github
可是这个问题有一个解决方案。 咱们能够在module目录中提取actions,getters和mutations。 全局actions,getters或mutations能够直接存储在store目录中。 应用程序结构以下所示:web
基本上,你仍然有可能使用全局actions,getter和mutations,但我并不推荐它,由于它并非真的有必要。 采用这种方法,咱们将拥有多个独立的文件。 聊天module的全部actions,getters和mutations将经过聊天目录内的索引导入。 这个module将被导入到全局store。 须要注意的是,你应该在模块内设置命名空间选项,以便你拥有适当的命名空间。 这是在store /index.js
文件中完成的:vuex
import Vue from 'vue'; |
在这个store里面,咱们有两个module:chat
和product
。 两个模块都包含actions,getters和mutations,并被导入到module的主module文件index.js
中,而后再次导出。 最后,导出的数据能够被store module使用。npm
这将注册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的范围,容器和组件对于每一个正在阅读代码的开发者都是清楚的。 因为在整个应用程序中使用关注点分离原则,所以能够快速找到每一个功能的业务逻辑而且功能很容易测试。
不一样结构的例子: