摘要: > * 原文地址:[structuring projects and naming components in react](https://hackernoon.com/structuring-projects-and-naming-components-in-react-1261b6e18d76) > * 原文做者:[Vinicius Dacal](https://hackernoon.cocss
React 做爲一個庫,不會決定你如何組織項目的結構。這是件好事,由於這樣咱們有了充分的自由去嘗試不一樣的組織方式而且選取最適合咱們的方式。可是從另外一個角度講,這可能會讓剛剛上手 React 的開發者產生些許困惑。react
我將會在本文爲你們展現我已經使用過一段時間而且效果不錯的方式,這些方式沒有經過從新造輪子來實現,而是經過將社區中的方案組合和提煉獲得。git
注意:這裏沒有什麼是絕對正確的!你能夠選擇你認爲容易理解,而且能夠適應或能夠改形成適應你的情景的方式。github
我最多見到的一個問題就是如何組織文件和目錄結構。在本文中,咱們假設你有一個 create-react-app
生成的最簡單的目錄結構。json
create-react-app
爲咱們生成了一個基礎的項目,包含根目錄還有諸如.gitignore
, package.json
, README.md
, yarn.lock
的文件。react-router
它還生成了 public
和 src
目錄,src
目錄就是咱們存放源代碼的目錄。app
看一下下面圖片所描述的結構:dom
本文咱們只會關注 src
目錄,全部在它以外的都會保持不變。編輯器
咱們能夠看到在 src
目錄下有 containers 目錄和 components 目錄:函數
src ├─ components └─ containers
可是這個方式會致使下面這些問題:
components
挪到 containers
目錄下,反之亦然。有一種基於這種方式的變種方式,在模塊的目錄下保持着兩個目錄的分離。
想象一下在你的應用中有一個 User 模塊,在此模塊下,你有兩個目錄去分離你的組件:
src └─ User ├─ components └─ containers
上述方式最小化了在兩個遙遠目錄下不斷切換的的問題,可是一樣增長了不少煩惱。當你的應用有很是多模塊的時候,你最終會可能會建立幾十個 containers
和 components
目錄。
因此咱們討論如何組織目錄和文件的時候,和組件是否被拆分爲展現型和容器型是無關的。也就是說,咱們會把全部的組件都放在 components
目錄下,除了頁面。
即便在目錄上拆分它們是沒必要要的,瞭解它們之間的差別性依然是有必要的。若是你對這個話題還有疑問,建議閱讀這篇文章:Presentational and Container Components。
在 components
目錄下,咱們經過模塊/特性(module/feature)的結構來組織文件。
在對用戶進行增刪改查的過程當中,咱們只會有一個 User 模塊。因此咱們的目錄結構會像下面這樣:
src └─ components └─ User ├─ Form.jsx └─ List.jsx
每當一個組件會有不止一個文件的時候,咱們會將這個組件和它對應的文件放在同一個文件夾下,而且使用同一個名字來命名。舉個例子:如今咱們有一個 Form.css
文件包含了 Form.jsx
的樣式,這時咱們的目錄結構會像這樣:
src └─ components └─ User ├─ Form │ ├─ Form.jsx │ └─ Form.css └─ List.jsx
測試用的文件和被測試的文件放在一塊兒,在上面這個例子中,
Form.jsx
的測試文件會放在同一個文件夾下而且命名爲Form.spec.jsx
除了經過模塊拆分組件,咱們還會在 src/components
放置一個 UI
目錄,用於存放全部通用的組件。
UI 組件不屬於任何一個模塊,須要足夠通用。它們應該能夠直接放在開源庫中,由於它們不包含任何特定應用的業務邏輯。常見的這類組件有:按鈕,輸入框,複選框,下拉選擇,模態框,數據可視化組件等等。
以上咱們瞭解瞭如何組織目錄結構和如何經過模塊來拆分咱們的組件,可是還有一個問題:如何命名它們?
這裏咱們說的是如何命名咱們的 class 或者定義組件的常量。
class MyComponent extends Component {} const MyComponent = () => {};
組件的命名在應用中應當清晰且惟一,這樣可讓它們能夠輕鬆被找到而且避免可能的困惑。
當應用在運行時發生錯誤或者經過 React 開發者工具調試時,組件的名字是很是方便易用的,由於錯誤發生的地方每每都伴隨着組件的名字。
咱們採用基於路徑的組件命名方式,即根據相對於 components
文件目錄的相對路徑來命名,若是在此文件夾之外,則使用相對於 src
目錄的路徑。舉個例子,組件的路徑若是是 components/User/List.jsx
,那麼它就被命名爲 UserList
。
若是文件名和文件目錄名相同,咱們不須要重複這個名字。也就是說,components/User/Form/Form.jsx
會命名爲 UserForm
而不是 UserFormForm
。
這樣的命名方式有如下幾點好處:
若是你的編輯器支持模糊搜索,只須要搜索 UserForm
就可讓你找到對應的文件:
若是你想要在目錄樹中搜索文件,能夠很容易地經過組件的名字定位到它:
遵循這種方式,你能夠根據組件的上下文環境來命名文件。想一下上面的 form 組件,咱們知道它是一個 User 模塊下的 form 組件,可是既然咱們已經把 form 組件放在了 User 模塊的目錄下,咱們就不須要在 form 組件的文件名上重複 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';
如今想象一下一個文件名中重複五到十次。
出於這樣的緣由,咱們認爲根據組件文件的上下文環境以及它的相對路徑來命名是更好的方式。
若是要對一個用戶作增刪改查的操做,咱們須要有用戶列表頁面,建立新用戶的頁面以及編輯已有用戶的頁面。
在應用中,經過使用組件相互組合的結果,就是一個頁面。理想狀態下,頁面應該不包含任何邏輯,而僅僅是一個函數式組件。
咱們以 src
目錄爲根目錄,將不一樣頁面分散在不一樣文件夾中。由於它們是根據路由定義而不是模塊來劃分紅組的。
src ├─ components └─ screens └─ User ├─ Form.jsx └─ List.jsx
假設咱們項目中在使用 react-router,咱們在 screens 目錄下放置 Root.jsx 文件,而且在其中定義咱們應用全部的路由。
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 目錄而且將 List 頁面和 Form 頁面放在裏面。這種方式使你看一眼 url 就可以輕鬆定位當前路由渲染的頁面。
像上面的例子中的建立和編輯一個用戶的路由同樣,一個頁面可能會被兩個不一樣的路由渲染使用。
你可能注意到了全部的組件都包含 Screen 做爲名稱的前綴。當組件在組件目錄外使用時,咱們須要使用它們相對於 src 目錄的路徑來命名。位於 src/screens/User/List.jsx
的組件應該被命名爲 ScreensUserList。
包括 Root.jsx 在內,咱們的目錄結構以下:
src ├─ components └─ screens ├─ User │ ├─ Form.jsx │ └─ List.jsx └─ Root.jsx
別忘了在 index.js 中引入做爲應用根組件的 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