1. 程式人生 > >面向React/Redux的可維護專案結構之旅【翻】

面向React/Redux的可維護專案結構之旅【翻】

當我開始學習Redux的時候,我被有關於Redux的大量討論和“最佳實踐”(可以在網上找到)震驚到,但是沒有太多的時間理解為什麼:Redux關於構建一個圍繞它的專案的方式不是很有見地,而且,當你試著弄清楚什麼樣的結構適合你的風格和你的專案時,你會遇到一些麻煩。

在這篇文章中,我想要分享一些關於我獲取到的舒適的Redux專案結構資訊。

這不是一個介紹/教程,需要具備一些Redux的基礎知識才能完整的理解這篇文章

而且,除了redux-sagas(它可以被redux-thunk或者你喜歡的處理非同步actions的庫替代),我將不會使用任何外部的Redux庫/實用程式

首先介紹: 按照“型別(type)”來組織檔案

當我開始使用Redux時,我從上到下的學習官方文件,我通過下面這種方式來組織我的專案:

src
├── components
├── containers
├── auth.js
└── product.js
├── actions (action creators)├── auth.js
└── product.js
├── types (action types)├── auth.js
└── product.js
└── reducers
├── auth.js
└── product.js

這個結構由官方Redux庫示例提出,而且在我看來,現在它依然是一個牢固的選擇。

這種組織結構最主要的缺點是,即使是新增一個小小的特性,最終可能會編輯幾個不同的檔案。

例如新增一個欄位(通過action來更新)到產品商店,意味著你必須進行如下操作:

  • types/product.js檔案中新增一個action型別
  • actions/products.js檔案中新增action creator
  • reducers/product.js檔案中新增一個欄位

而且這不是最終結果!當你的應用程式增長時,你可能需要新增其它的目錄:

├── sagas
├── auth.js
└── product.js
└── selectors
   ├── auth
.js └── product.js

因此,在我的專案中引入redux-saga之後,我意識到這變的更加難以維護,我開始尋找替代方案。

一個不同的方法: 按照特徵(feature)來組織檔案

上述專案結構的一個替代方法是按特徵方法對檔案進行分組:

src
 ├── components
 ├── auth
 ├── container.js
 ├── actions.js
 ├── reducers.js
 ├── types.js
 ├── sagas.js
 └── selectors.js
 └── product
    ├── container.js
    ├── actions.js
    ├── reducers.js
    ├── types.js
    ├── sagas.js
    └── selectors.js

這個方法在React社群中已經被不同有趣的文章推廣,並且它被用於最常見的一個React boilerpalte專案中。

乍一看,這個結構對我來說似乎是合理的,因為我遵循React的元件概念,將一個元件(the container),它的state(the store)和它的行為(the actions)封裝在一個資料夾中。

當在一個更大的專案中使用它時,我發現這個方法也不能帶來所有的你想要的結果: 如果你使用Redux,你可能正在做的是在你的應用程式之間共享一個分支的儲存...你可以很容易的看到這與此結構提出的封裝概念衝突。例如,從product container(容器)分派一個action可能會對cart container產生副作用(如果cart reducer以某種方式對action做出反應)。

你還必須小心,不要陷入另一個概念陷阱: 不要覺得被迫把一個Redux store繫結到一個container,因為你可能最終會使用Redux,即使你應該選擇使用簡單的setState方法。

Ducks的救援

在按照feature分組的小冒險之後,我回到了我的初始專案結構,我注意到使用Sagas來處理非同步流有一個有趣的副作用,將90%的action creators轉換為一句話:

const login =(email, password)=>({ type: LOGIN_REQUEST, email, password })

在這樣的情況下,每一個action creator,action type和reducer都有一個專門的檔案似乎有點過度,因此我決定嘗試“Ducks”方法。

Ducks推薦將reducers,action types和actions繫結到同一個檔案中,導致減少樣板:

// src/ducks/auth.jsconst AUTO_LOGIN ='AUTH/AUTH_AUTO_LOGIN'const SIGNUP_REQUEST ='AUTH/SIGNUP_REQUEST'const SIGNUP_SUCCESS ='AUTH/SIGNUP_SUCCESS'const SIGNUP_FAILURE ='AUTH/SIGNUP_FAILURE'const LOGIN_REQUEST ='AUTH/LOGIN_REQUEST'const LOGIN_SUCCESS ='AUTH/LOGIN_SUCCESS'const LOGIN_FAILURE ='AUTH/LOGIN_FAILURE'const LOGOUT ='AUTH/LOGOUT'const initialState ={
  user:null,
  isLoading:false,
  error:null}exportdefault(state = initialState, action)=>{switch(action.type){case SIGNUP_REQUEST:case LOGIN_REQUEST:return{...state, isLoading:true, error:null}case SIGNUP_SUCCESS:case LOGIN_SUCCESS:return{...state, isLoading:false, user: action.user }case SIGNUP_FAILURE:case LOGIN_FAILURE:return{...state, isLoading:false, error: action.error }case LOGOUT:return{...state, user:null}default:return state
  }}exportconst signup =(email, password)=>({ type: SIGNUP_REQUEST, email, password })exportconst login =(email, password)=>({ type: LOGIN_REQUEST, email, password })exportconst logout =()=>({ type: LOGOUT })// src/ducks/product.jsconst GET_PRODUCTS_REQUEST ='PRODUCT/GET_PRODUCTS_REQUEST'const GET_PRODUCTS_SUCCESS ='PRODUCT/GET_PRODUCTS_SUCCESS'const GET_PRODUCTS_FAILURE ='PRODUCT/GET_PRODUCTS_FAILURE'const initialState ={
  products:null,
  isLoading:false,
  error:null}exportdefault(state = initialState, action)=>{switch(action.type){case GET_PRODUCTS_REQUEST:return{...state, isLoading:true, error:null}case GET_PRODUCTS_SUCCESS:return{...state, isLoading:false, user: action.products }case GET_PRODUCTS_FAILURE:return{...state, isLoading:false, error: action.error }default:return state
  }}exportconst getProducts =()=>({ type: GET_PRODUCTS_REQUEST })

第一次採用這種語法時,我就愛上它了。 它移除了很多不必要的樣板檔案,而且你很容易的通過改變一個檔案來新增一個action或者一個欄位到reducer。

不幸的是,使用ducks開始很快但也有限制,因為單獨暴露每個action creator和action type有一些令人討厭的副作用。

  • 在一個更大的containers中,你會得到一個巨大的匯入的actions列表,這個列表只使用一次(作為mapDispatchToProps的引數):

    import{ signup, login, resetPassword, logout,}fromducks/authReducer
  • 你將不能直接的將duck檔案的所有actions傳遞到mapDispatchToProps中。使用import * as actions沒有作用,因為你最終也匯入了reducer。

  • 你會浪費一個超級寶貴的變數名,只是為了將actions傳遞給mapDispatchToProps。你甚至不會做類似這樣的事情:const {login} = this.props,因為你已經通過將login變數分配給從duck檔案匯入的action creator定義了login變數。
  • 在更大的sagas中,你最終會使用很多不同的不知道上下文的actions(每一次,你必須滾動到頂部引入它們)。

自定義ducks

對於以上問題,我的解決方法很簡單: 不是單獨的暴露action types和action creators給外部引用,而是將它們組織到一個types和actions物件中之後在匯出:

// src/ducks/auth.jsexportconst types ={
  AUTO_LOGIN:'AUTH/AUTH_AUTO_LOGIN',
  SIGNUP_REQUEST:'AUTH/SIGNUP_REQUEST',
  SIGNUP_SUCCESS:'AUTH/SIGNUP_SUCCESS',
  SIGNUP_FAILURE:'AUTH/SIGNUP_FAILURE',
  LOGIN_REQUEST:'AUTH/LOGIN_REQUEST',
  LOGIN_SUCCESS:'AUTH/LOGIN_SUCCESS',
  LOGIN_FAILURE:'AUTH/LOGIN_FAILURE',
  LOGOUT:'AUTH/LOGOUT'}exportconst initialState ={
  user:null,
  isLoading:false,
  error:null}exportdefault(state =
            
           

相關推薦

面向React/Redux維護專案結構

當我開始學習Redux的時候,我被有關於Redux的大量討論和“最佳實踐”(可以在網上找到)震驚到,但是沒有太多的時間理解為什麼:Redux關於構建一個圍繞它的專案的方式不是很有見地,而且,當你試著弄清楚什麼樣的結構適合你的風格和你的專案時,你會遇到一些麻煩。

瘋狂的訂餐系統-軟體需求分析挑戰

摘要: 說教性質的需求分析理論,各位看了也白看,所以咱們就來一個真實個案——“訂餐系統”體驗一下。 “訂餐系統”貌似簡單,但陷阱重重,各種需求分析的經典場景將會一一重現,各位做好準備接受這個挑戰沒有? 特別宣告:如需轉載此文,請給出指向本網站的連線,如下:作者:張傳波摘自:http://www.

ClouderaSpark-sql部署

參考 概述 由於cloudera自帶的spark-yarn不支援沒有spark-sql(也許是cloudera推廣自己的impala吧),但是如果我們線上要用spark直接寫原生的必然效率低很多 這裡簡單描述下自己遇到的兩個超級大坑

實踐webpack+es6+react+redux+antd構建專案(三) 配置proxy代理

上一篇是關於引入react,redux,antd的,專案已經可以正常運行了,但是拿到的都是定義的死資料。這篇文章講一下配置代理,調mock資料。 簡便起見,我使用的是Easy Mock,定義好mock資料,在專案中引入使用。 先說一下Easy Mock的使用方法, 建立一個專案,

實踐webpack+es6+react+redux+antd構建專案(二) reactredux,antd引入

上一篇文章是關於從零構建一個webpack專案,基本已經啟動成功了,這篇文章將會講一下在專案中使用目前最流行的React框架 1、引入babel相關 現代前端基本都是以es6為規範進行開發,所以我們專案中也就需要引入es6。es6是需要使用babel進行轉換的,瀏覽器才能識別 npm

實踐webpack+es6+react+redux+antd構建專案(一) webpack配置

        在網上看到過很多教程,都是從零開始構建一個專案,每次看著都浩浩蕩蕩的開始跟著部落格一步一步走,但是總是很難成功。自己一直想要從零構建一個專案來實踐一下,瞭解一下偏底層的配置。最近比較有時間,就又一次開始了。不過這次沒有盲目的在網上找教程,而

Flux、Reduxreact-redux發展衍變三部曲react-redux解讀

首先要知道,Redux只是一種MVC的機制,它可以執行在任何的前端框架中。react-redux則是對Redux的核心模組(如store)進行封裝,並加入了一些有利於react開發的模組,這樣就可以更便捷的在react中運用redux。 本篇將詳細介紹react-redux開發中的常用模組。

Flux、Reduxreact-redux發展衍變三部曲Redux解讀

續上篇,在Flux後,為了更好的實現MVC,Redux模式出現。 不同於 Flux ,Redux 不再有 dispatcher 的概念(Store已經集成了dispatch方法)。其次它依賴純函式來替代事件處理器(即原來Flux中Dispatcher.register((actio

React+Redux開發實戰專案美團App,沒你想的那麼難

README.md 前言 開始學習React的時候,在網上找了一些文章,讀了官網的一些文件,後來覺得React上手還是蠻簡單的, 然後就在網上找了一個React實戰的練手專案,個人學完之後覺

在谷歌瀏覽器中除錯React+Redux構建的專案,找不到原始檔的解決辦法

  前端除錯專案時,專案是基於webpack打包的,所以在谷歌瀏覽器中,只能找到打包後的檔案,而不是打包之前的專案原始檔,這樣不便於前端的除錯。怎麼在Source下開啟想要除錯的檔案呢,有如下解決辦法。   一 、非webpack構建的專案下,快速檢視相關的程式碼     F12開啟除錯面板、開啟source

資料結構二叉樹(一)B樹、B-樹、B+樹、B*樹介紹,和B+樹更適合做檔案索引的原因

     今天看資料庫,書中提到:由於索引是採用 B 樹結構儲存的,所以對應的索引項並不會被刪除,經過一段時間的增刪改操作後,資料庫中就會出現大量的儲存碎片,這和磁碟碎片、記憶體碎片產生原理是類似的,這些儲存碎片不僅佔用了儲存空間,而且降低了資料庫執行的速度。如果發現索引

資料結構二叉樹(二)B+樹比B樹更適合做檔案索引的原因

 原因:相對於B樹, (1)B+樹空間利用率更高,可減少I/O次數, 一般來說,索引本身也很大,不可能全部儲存在記憶體中,因此索引往往以索引檔案的形式儲存的磁碟上。這樣的話,索引查詢過程中就要產生磁碟I/O消耗。而因為B+樹的內部節點只是作為索引使用,而不像B-樹那樣每個節點都需要儲存硬碟指標。

java版資料結構與演算法分析學習前言

一.資料結構和演算法概述?【框範圍】 基礎資料結構主要包括表【陣列+連結串列】、棧、佇列【散列表】、樹、圖、堆。高階資料結構包括伸展樹、紅黑樹、確定性跳躍表、AA樹、treap樹、k-d樹、配對堆。

資料結構(一)順序儲存結構來實現線性表

用陣列(順序儲存結構)來實現線性表 該資料結構具有如下功能: 初始化 獲取資料 在表尾新增資料 彈出表尾資料 設定指定位置資料 刪除指定位置資料 在指定位置插入資料 優點 1、無須為表示表中元素之間的邏輯關係而增加額外的儲存空間 2、可以快速地存取表中任一位置的元素

資料結構線性表總結

前言: 這幾天在學習資料結構,引用書中的一句話:資料結構 = 程式 + 演算法;本篇部落格作為一個知識的總結,希望給大家帶來幫助. 核心 : 線性表定義 從字面意義來看,呈線性,所以說

混合開發(Hybrid App) Ionic-- 認識Ionic,搭建開發環境,建立專案

寫在前面 目前市面存在的移動開發方式有原生應用、混合應用、原生應用三種,對於這種方式可以做以下對比。      認識ionic ionic 是一個用來開發混合手機應用的,開源的,免費的程式碼庫,具有以下特點。 1.ionic 基於Angular語法,簡單易學。 2.io

linuxawk 轉載

填充 too usr log .com 格式化文本 software 但是 field 史上最好用的免費翻薔利器 簡介 awk是一個強大的文本分析工具,相對於grep的查找,sed的編輯,awk在其對數據分析並生成報告時,顯得尤為強大。簡單來說awk就是把文件逐行的讀入,以

python起步Hello World

pythonpython打卡第一天: Python是什麽,我相信網上一搜一大堆,這裏我就不介紹了。下面是我的學習記錄: 學習一門程序語言,首先大家都會去了解這個語言的發展歷史,然後開始先用這個程序語言輸出神聖宣言“hello world!”。就像宣誓一樣,標誌著你開始學習這門程序語言。把它當做你的信仰一樣,去

Linux學習命令1

active acc 同時 計算機 用戶名 幫助 理解 否則 解釋 Linux學習之命令【1】 一·命令總覽?????? 1.基本簡單命令??????? ?1.0 pwd1.1 echo ?????????1.2 dat

BZOJ2194 快速傅立葉fft

class real puts edge max math long long cst include 題目 請計算C[k]=sigma(a[i]*b[i-k]) 其中 k < = i < n ,並且有 n < = 10 ^ 5。 a,b中的元素均為小於等