2. 架構完整的 React 專案結構


.
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.css
│   ├── App.test.tsx
│   ├── App.tsx
│   ├── index.css
│   ├── index.tsx
│   ├── logo.svg
│   ├── react-app-env.d.ts
│   ├── serviceWorker.ts
│   └── setupTests.ts
├── README.md
├── package.json
├── tsconfig.json
└── yarn.lock

這是 create-react-app 預設的專案結構
我們可以看到 src/ 裡面基本上是沒有特別的結構
因為 create-react-app 本身並沒有提供專案的推薦結構
我們必須要自己進行規劃才行

這篇主要會分享平常我們使用的結構
由於最近發現一些更好的模式(不過還沒實戰)
所以會先暫時分享目前的版本

常見基本架構

一般來說,我們會使用以下的架構做為專案的初始化

src/
    components/
    pages/
    images/

components 裡面主要放所有的元件實作
pages 裡面則是放頁面層級的元件
images 則是放需要引入的圖片

這邊特別將 pages 拉出來是因為我們不能讓 routing 跟 components 相依
對於任一個 components 來說,應該是可以放在任何地方的
所以只要有使用到 routing 的部分,像是 useRoute 或是 withRouter(Page)
都應該是綁定到 page 層級,而 page 層級還需要簡單到可以讓人直接了解結構
ex.

const Page = () => {
    return (
        <div>
            <Header />
            <Content>
                <PageTitle />
                <Carousel />
            </Content>
            <Footer />
        </div>
    )
}

以功能來分類元件

再來是 components 裡面的結構,這邊在網路上有各式各樣的說法
有使用種類來分類的:

src/
    components/
        buttons/
            PrimaryButton.tsx
            SecondaryButton.tsx
            DefaultButton.tsx
        cards/
            PaymentCard.tsx
            UserCard.tsx
            ProductCard.tsx

也有使用功能來分類的:

src/
    components/
        payment/
            PaymentCard.tsx
            PaymentButton.tsx
        user/
            UserCard.tsx
            UserProfile.tsx

經過多個專案後,我們選擇了以功能來分類元件
因為如果是以種類來分類的話,常常會有功能調整時,要去很多個資料夾找元件的情況
如果是以功能區分,不論是新增或是更新功能時都能夠更好的管理

Container/Presenter Pattern

這個模式是我們目前最常用的模式,不過也是我們最近想要調整的模式
概念是將所有資料層與展示層進行分離,讓資料與樣式分開處理

src/
    components/
    containers/

這個模式在大部分情況是沒問題的,不過也在近期發現這不是個很好的模式
雖然我們確實地將資料處理的部分拉出了 component
但也因為如此,我們常常會在 container 的地方修正樣式
不然的話就會變成這種空殼 container:

const Container = () => {
    // handle data
    return <Component />
}

因為寫這篇的同時查了很多資料,發現這個模式已經不太推薦了
所以只有很簡短地介紹,避免大家太認真看待這部分

PS. 由於 Hook 的成熟,Dan Abramov 在 2019 年後開始不建議使用,但是目前在社群上還是有各種聲音,未來看看是否能補充使用 Hook 取代 Container 抽象層的方式。

Custom Hook

由於 hook 出來後,我們可以將許多狀態與資料的管理抽離元件
於是這邊仍建議使用功能來區分 hook

src/
    hooks/
        payment.ts
        user.ts

建議一個檔案裡面有多個 hook,例如:

# payment.ts
export const useCreditCard = () => {}
export const useATM = () => {}

但這邊要強調一件事,就是不要把所有的 hook 都放到這邊
如果 hook 只跟該 component 有關,請直接放在該 component 即可,例如:

const useForm = () => {}
const SignInForm = () => {
    const { onSubmit } = useForm()
    return (
        <form onSubmit={onSubmit}>
            <input />
            <button type="submit" />
        </form>
    )
}
export default

完整專案架構

public/
src/
    pages/
        {PageName}.tsx
    components/
        {function}/
            {ComponentName}.tsx
    contexts/
        {Context}.tsx
    hooks/
        {function}.tsx
    utils/
        {function}.tsx
    images/
        {function}/
            {image_name}.{png|jpg|...}
    types/
        {type}.d.ts

PS. types/ 後續文章會介紹到,主要是定義型別用

結語

由於主要是在強調 TS 如何撰寫 React
所以這篇的部分比較短(但非常重要!!!)
因為今天實在太忙,所以篇幅較短
希望比賽後能夠好好把這篇寫好

murmur...
才第二天就要忙到快放棄了,撐下去啊....








你可能感興趣的文章

What is the concept of handle in Java?

What is the concept of handle in Java?

propTypes、以 state 為中心去思考

propTypes、以 state 為中心去思考

【筆記】 初學Node.js + Express.js 建立簡易餐廳清單

【筆記】 初學Node.js + Express.js 建立簡易餐廳清單






留言討論