大型Vuex應用程序的目錄結構

譯者按: 聽前端大佬聊聊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';
import Vuex from 'vuex';
import chatModule from './modules/chat/index';
import productsModule from './modules/products/index';

Vue.use(Vuex);

export default new Vuex.Store({
modules: {
chat: chatModule,
products: productsModule,
},
});

在這個store裏面,咱們有兩個module:chatproduct。 兩個模塊都包含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.jsVuex的反應性更新。 以後,該屬性將做爲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.vueChatListElement.vue就是在那裏從組件中獲取數據並觸發到store的行爲,該store全局鏈接到Vue.js實例。最大的問題是爲何這些組件不在組件目錄中。緣由是這些組件是專門爲此功能而製做的。若是他們將被從新用於其餘功能,那麼我會考慮將其移入組件目錄。基本上這裏的問題是,若是組件以某種方式被重用。而後咱們應該將組件重構到共享組件目錄中。如今來看store。它與其餘模式基本相同,但移入本地目錄store。要註冊它,咱們使用Vuex的registerModule函數。該功能將動態註冊Vuex模塊。一般它被用於插件,但咱們將在這裏使用它來更好地分離問題。在index.vue文件中,咱們能夠經過Vue.js訪問生命週期函數,而且在建立的函數中,咱們能夠安全地建立module。

import { mapGetters } from 'vuex';
import store from './_store';
import ChatList from './_components/ChatList';

export default {
name: 'ChatModule',
components: {
ChatList,
},
computed: {
...mapGetters({
messages: '$_chat/messages',
}),
},
created() {
this.$store.registerModule('$_chat', store);
},
mounted() {
this.$store.dispatch('$_chat/getMessages');
},
};

咱們用$ _做爲前綴,代表這個module是私有的,由於它只在module中可用。 註冊後,該store將填充到咱們的全局Vuex store。 從那裏開始,咱們能夠在組件內使用這些Vuex功能。 要註冊store,咱們須要以某種方式將Vuex功能綁定到Vue.js實例。 這能夠經過建立一個空的Vuex store,導出並將其附加到Vue.js構造函數來輕鬆完成。 查找這些文件以獲取想法(store/index.jsmain.js)。

若是咱們發現本身須要某種全局store,我會在store目錄下建立一個Vuex module,並使用推薦的結構。 例如,若是咱們須要在應用程序內部的不一樣位置進行身份驗證,最好以不與容器耦合的方式進行共享。 這對於擁有共享的Vuex module將是一個很好的實例。

一些問題:可能不清楚哪些module須要全局或本地可用,並且很難作出決定。 它也很難找到應該是全局的組件,但基本上,全部通用組件應該位於由不一樣module使用的目錄中。 維持這種結構真的很難,但最終我認爲爲了擴展應用程序是值得的。 另外一個問題是命名。 你如今有遍及整個地方的組件目錄。 將模塊_components中的目錄命名爲私有組件可能更好,但這是我的偏好。

這個結構的一個很好論點是modules在某種程度上是能夠提取的。 若是一個功能變得太大,你能夠經過在src/modules目錄內的目錄中建立一個module來提取它,而後製做一個npm軟件包。 您須要導出的惟一東西是容器組件。 而後,這個npm包能夠託管在公司的註冊表中,也能夠公佈在npm上。 只要確保以某種方式使Vuex模塊的行爲可配置。 另外一個很好的論點是測試能夠用特徵範圍的方式編寫。

最積極的論點是Vuex module的範圍,容器和組件對於每一個正在閱讀代碼的開發者都是清楚的。 因爲在整個應用程序中使用關注點分離原則,所以能夠快速找到每一個功能的業務邏輯而且功能很容易測試。

不一樣結構的例子:

相關文章
相關標籤/搜索