如何合理佈置React/Redux的目錄結構

凹凸卿發表於2017-03-17

Git blog地址: github.com/asd0102433/…
喜歡的同學star支援一下,長期更新

專案的結構改過很多次,每次根據需求的複雜度慢慢的修改,總結下幾種結構的特點。

按照檔案型別劃分,不是很複雜的專案可以這樣規劃。

|—— actions
    |—— CommandActions.jsx
    └── newAction.jsx      <- here
|—— components
    |—— Command.jsx
    └── newComponent.jsx   <- here
|—— containers
    |—— Command.jsx
    └── newContainers.jsx  <- here
└── reducers
    |—— command.jsx
    └── newReducers.jsx    <- here複製程式碼

上面這種是官方demo redux.js.org/docs/advanc…
結構,actions,reducers,containers中放著每個模組的對應的結構檔案,看過去很清晰,但是有一個麻煩的地方,就是當你新增一個元件的時候你就需要在3個目錄下操作,以及跨檔案的管理對應的檔案,有點不方便。


按照元件劃分,一個元件包含自身action,reducers,style等相關的檔案,這樣在修改某些檔案的時候就非常的容易。對於專案不存在很複雜的非同步邏輯等可以參考這樣的結構。

    |—— app
        |—— App.jsx
        |—— reducers.jsx
        |—— routes.jsx
    |—— home
        |—— index.jsx
        |—— Home.jsx
        |—— HomeActions.jsx
        └── HomeReducer.jsx
    |——  product
        |—— index.jsx
        |—— ProductList.jsx
        |—— ProductActions.jsx
        └── ProductReducer.jsx複製程式碼

如果專案十分的巨大,大量的模組用以上的2個方案不可行,首先複用模組和特定場景下的模組需要分開進行處理,頁面的佈局view必須整理出來,可以參考react boilerplate

|——app
   |—— component                # 這裡放的都是公共部分的元件
       |—— Header               # 使用 styled-components 來定義基礎元件
       └── Fotter
   |—— containers               # 頁面容器
       |—— HomePage
           |—— ...
           |—— index.js        # 組織頁面的結構, 私有模組和公共模組構成
           |—— reducer.js      # Home下的reducer邏輯
           |—— component     # 私有模組
           |—— sagas.js        # Home下的非同步資料
           |—— action.js
           |—— ...
           └── reducers.js   # reducer複製程式碼

可以說這套專案結構很適合大型專案的組織,component下面包括了大量的通用元件,不管是專案的平臺移植,模組複用都很好管理。containers下如HomePage/index.js有複用的模組以及頁面場景下特殊的模組構成,同時index.js還負責模組跟redux store資料的連結,對應的每個場景都擁有自身saga,reducer等。構建大型的專案結構參考這個也是一個非常棒的。


react boilerplate 確實可以解決大型專案的結構問題,但是component 和佈局結構混合在一起,並沒有分離出去,下面這種結構分離了元件,佈局模組,更好的管理專案(檔案結構同時也增加了複雜度)。

|── src/
|  |── views
   |   |—— Home.js         # Home Page 頁面
   |   |—— HomeRedux.js    # Home Redux 集合
   |   └── Detail.js       # Detail Page 頁面
   |—— redux
   |   └── reducers.js     # 統一了views下的所有reducer
   |—— layouts            # layouts 負責整個app 的佈局結構
   |   |—— Frame.js
   |   |—— Nav.js      
   |—— components
   |   |—— Common          # 通用元件
   |   |—— Home            # Home Page下用到的元件
   |   |   |—— Preview.js
   |   |   └── PreviewRedux.js   # Preview元件用到的reducer, 以及action複製程式碼

layouts 程式碼

return (
      <div className="frame">
        <div className="header">
          <Nav />
        </div>
        <div className="container">
          // View下面的Page 都會在此
          { this.props.children }
        </div>
      </div>
    );複製程式碼

有了layout頁面的佈局細分就更加直觀,明晰了。在管理reducer的時候會相對的麻煩,views/ 下的主入口頁面不但負責頁面的結構,還需要整合 components/Home/ 檔案下所有的模組的reducer,需要跨檔案的處理。Home下Preview元件的reducer、action合併在一起,方便了修改同時控制自身的reducer業務邏輯(對於細分到元件的reducer,action本身邏輯也不會很龐大)。
比如 listReducer -> List ,確實很適合大型團隊的分工合作。


接下來我們來看看react-starter-kit
github.com/bodyno/reac…

github.com/bodyno/reac… 這個專案的結構使用的是 fractal(不規則碎片形:適合大型專案)*,方法的分組主要是依照特性而不是檔案型別。注意,這個目錄結構只是一個指引,並不一定要按這個來。這種結構諧在讓程式更容易擴充套件

├── src                      # 程式原始檔
│   ├── main.js              # 程式啟動和渲染
│   ├── components           # 全域性可複用的表現元件(Presentational Components)
│   ├── containers           # 全域性可複用的容器元件
│   ├── layouts              # 主頁結構
│   ├── store                # Redux指定塊
│   │   ├── createStore.js   # 建立和使用redux store
│   │   └── reducers.js      # Reducer註冊和注入
│   └── routes               # 主路由和非同步分割點
│       ├── index.js         # 用store啟動主程式路由
│       ├── Root.js          # 為上下文providers包住元件
│       └── Home             # 不規則路由
│           ├── index.js     # 路由定義和程式碼非同步分割
│           ├── assets       # 元件引入的靜態資源
│           ├── components   # 直觀React元件
│           ├── container    # 連線actions和store
│           ├── modules      # reducers/constants/actions的集合
│           └── routes **    # 不規則子路由(** 可選擇的)複製程式碼

routes 作為主入口,並且把所有路由對應的元件,assets, modules全部都集中在了一起,更像mvc的命名組織。reducer、action整合為modules, components作為view,container連線actions和store。

export const createRoutes = (store) => ({
  path: '/',
  component: CoreLayout,
  indexRoute: Home,
  childRoutes: [
    CounterRoute(store),
    ZenRoute(store),
    ElapseRoute(store),
    RouteRoute(store),
    PageNotFound(),
    Redirect
  ]
})複製程式碼

一個Counter模組包含如下:

Counter/
    components/   # 頁面的元件
     containers/   # view 和 modules 資料對接
    modules/      # 包含對應的 reducer, action
    index.js      # 頁面入口,定義path複製程式碼

index.js 自動的注入reducer 到store,這樣在頂層的store就無需要手動去整合每個模組自身的reducer。程式碼如下:

// 匯入對應的redicer
const reducer = require('./modules/counter').default
 /*  Add the reducer to the store on key 'counter'  */
injectReducer(store, { key: 'counter', reducer })複製程式碼

看完這套方案真的非常酷,個人覺得多人開發專案迭代產品更新,手動的修改整合頂層reducer是一件不好的事情。

專案的結構並非要跟上面相同,根據自己的需求和理解來構建自己的專案也是一件非常美好的事情。

相關文章