React 做爲一個庫,它沒有規定項目的總體結構。這很好,由於它給了咱們自由去嘗試不一樣的方法,並適應更適合咱們的方式。另外一方面,這可能會給React領域的開發人員帶來一些困惑。css
我將會在本文爲你們展現我已經使用過一段時間而且效果不錯的方式,這些方式沒有經過從新造輪子來實現,而是經過將社區中的方案組合和提煉獲得。前端
想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等着你!react
我常常遇到的一個問題是如何組織文件和目錄結構。在這篇文章中,咱們認爲你已有一個最小的結構,就像用 create-react-app
建立的結構同樣。git
create-react-app
爲咱們生成了一個基礎的項目,包含根目錄還有諸如.gitignore
, package.json
, README.md
, yarn.lock
的文件。github
它還生成 public
和src
目錄, src
目錄是咱們保存源代碼的地方。json
請看下面的圖片,以及描述的結構:react-router
在這篇文章中,咱們只關注src
目錄,src 以外保持不變。app
你可能已經在某些項目的根目錄下看到了容器和展現組件之間的分離。個人意思是,在src
中,在 src 目錄下有 containers
目錄和 components
目錄:dom
src ├─ components └─ containers
可是,這種方法有一些問題,以下所示:編輯器
components
挪到 containers
目錄下,反之亦然。containers
和 components
目錄下來回切換,由於一個獨立特性有兩種不一樣類型的組件是再正常不過的事情了。還有一種方法,在模塊內部保存containers
和components
分離:
src └─ User ├─ components └─ containers
上述方法最大限度地減小了在項目樹中不一樣層級目錄切換的問題。然而,它會增長不少噪音。根據你的應用程序有多少模塊,你最終會建立幾十個containers
和components
目錄。
出於這些緣由,當咱們談論組織目錄和文件時,經過展現與容器的概念來拆分組件是可有可無的。 也就是說,除頁面外,咱們將把全部組件放在 components
目錄下。
即便在目錄上區分展現組件和容器組件是沒有太多必要的,瞭解它們之間的差別性依然是有必要的。若是你對這個話題還有疑問,建議閱讀這篇文章: Presentational and Container Components
在components
目錄中,咱們按模塊/功能對文件進行分組。
在用戶的增刪改查中,咱們只有User
模塊,結構是這樣的
src └─ components └─ User ├─ Form.jsx └─ List.jsx
當組件由多個文件組成時,咱們將此組件及其文件放在具備相同名稱的目錄下。 例如:假設有一個包含Form.jsx
樣式的Form.css
。 在這種狀況下,你的結構以下:
src └─ components └─ User ├─ Form │ ├─ Form.jsx │ └─ Form.css └─ List.jsx
測試文件與被測試的文件保持一致。在上面的例子中,Form.jsx
的測試文件會放在同一個文件夾下而且命名爲Form.spec.jsx
除了經過模塊拆分組件以外,咱們還在src/components
中包含一個 UI
目錄,以保留其中的全部通用組件。
UI 組件是通用的組件,不屬於模塊。 它們是能夠保留在開源庫中的組件,由於它們沒有來自特定應用程序的任何業務邏輯。 這些組件的示例包括:按鈕,輸入,複選框,選擇,模態框,數據可視化組件等等。
上面咱們看到了如何構建目錄並按模塊分離咱們的組件。 可是,還有一個問題:如何命名它們?
當咱們談論命名組件時,它涉及咱們給類或定義組件的常量名稱:
class MyComponent extends Component { } const MyComponent () => {};
如上所述,咱們爲組件提供的名稱應該在應用程序中清晰且獨特,以便更容易找到並避免可能的混淆。
當咱們須要使用工具做爲React Dev工具進行調試時,以及當應用程序中發生運行時錯誤時,組件的名稱很是方便,錯誤老是與發生錯誤的組件名一塊兒出現。
咱們採用基於路徑的組件命名方式,即根據相對於 components
文件目錄的相對路徑來命名,若是在此文件夾之外,則使用相對於 src
目錄的路徑。舉個例子,組件的路徑若是是 components/User/List.jsx
,那麼它就被命名爲 UserList
。
當文件位於具備相同名稱的組件中時,咱們不須要重複該名稱。 也就是說,components/User/Form/Form.jsx
將被命名爲UserForm
而不是UserFormForm
。
上面的模式有一些好處,咱們能夠在下面看到:
若是編輯器支持模糊搜索,只需搜索名稱UserForm
就能夠找到正確的文件
若是你想要在目錄中搜索文件,能夠很容易地經過組件的名字定位到它:
按照該模式,能夠始終根據文件的上下文爲組件命名。考慮到上面的表單,咱們知道它是一個用戶表單,可是因爲咱們已經在 User
目錄中 ,因此不須要在組件文件名中重複這個單詞。所以,咱們只將它命名爲Form.jsx
。
我最初使用 React 的時候喜歡用完整的名字來命名文件,可是這樣會致使相同的部分重複太屢次,同時引入時的路徑太長。來看看這兩種方式的區別:
import ScreensUserForm from './screens/User/UserForm'; // vs import ScreensUserForm from './screens/User/Form';
在上面的示例中,可能沒法看到從一種方法到另外一種方法的優點。 可是應用程序名稱多了話,就能夠看到差別, 以下:
import MediaPlanViewChannel from '/MediaPlan/MediaPlanView/MediaPlanViewChannel.jsx'; // vs import MediaPlanViewChannel from './MediaPlan/View/Channel';
想象一下名稱重複十幾二十次的樣子。
所以,咱們根據文件 的上下文來命名文件,根據組件的相對位置來命名組件是一種更好的方式。
屏幕,顧名思義,就是咱們在應用程序中展現出來的樣子。
若是要對一個用戶作增刪改查的操做,咱們須要有用戶列表頁面,建立新用戶的頁面以及編輯已有用戶的頁面。
咱們將screens
保存在src
根目錄中的單獨文件夾中,由於它們將根據路由定義而不是模塊進行分組:
src ├─ components └─ screens └─ User ├─ Form.jsx └─ List.jsx
考慮到項目使用react-router
,咱們將文件Root.jsx
放在在screens
目錄下,並在其中定義全部應用程序路由。
Root.jsx 的代碼可能像下面這樣:
import React, { Component } from 'react'; import { Router } from 'react-router'; import { Redirect, Route, Switch } from 'react-router-dom'; import ScreensUserForm from './User/Form'; import ScreensUserList from './User/List'; const ScreensRoot = () => ( <Router> <Switch> <Route path="/user/list" component={ScreensUserList} /> <Route path="/user/create" component={ScreensUserForm} /> <Route path="/user/:id" component={ScreensUserForm} /> </Switch> </Router> ); export default ScreensRoot;
請注意,咱們將全部頁面放在一個目錄中,這個目錄以路由名稱命名,user/ -> User/
。嘗試爲每一個父級路由創建一個目錄,在這個目錄中組織子路由。 在這種狀況下,咱們建立了User
目錄,並將List
頁面和Form
頁面放入其中。這種方式使你看一眼 url 就可以輕鬆定位當前路由渲染的頁面。
單個頁面可用於渲染兩條不一樣的路線,如上所述,其中包含用於建立和編輯用戶的路線。
你可能會注意到全部組件都將Screen
做爲其名稱的前綴。 當組件位於components
目錄以外時,咱們應該根據它到src
文件夾的相對路徑來命名。 位於src/screens/User/List.jsx
的組件應命名爲ScreensUserList
。
建立 Root.jsx 後,目錄的結構以下:
src ├─ components └─ screens ├─ User │ ├─ Form.jsx │ └─ List.jsx └─ Root.jsx
若是你對一個頁面長什麼樣子還有疑問,看看下面的示例,它就是用戶表單的頁面。
import React from 'react'; import UserForm from '../../components/User/Form/Form'; const ScreensUserForm = ({ match: { params } }) => ( <div> <h1> {`${!params.id ? 'Create' : 'Update'}`} User </h1> <UserForm id={params.id} /> </div> ); export default ScreensUserForm;
最後,咱們的應用程序結構以下:
src ├─ components │ ├─ User │ │ ├─ Form │ │ │ ├─ Form.jsx │ │ │ └─ Form.css │ │ └─ List.jsx │ └─ UI │ └─ screens ├─ User │ ├─ Form.jsx │ └─ List.jsx └─ Root.jsx
src/components
src/components/UI
/user/list
路由地址來講,咱們會有一個頁面在 /src/screens/User/List.jsx
。src/components/User/List.jsx
的組件將被命名爲UserList
。 位於src/screens/User/List
的組件將命名爲ScreensUserList
src/components/User/List/List.jsx
位置的組件會被命名爲 UserList
而不是 UserListList
。你的點贊是我持續分享好東西的動力,歡迎點贊!