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...
才第二天就要忙到快放棄了,撐下去啊....








你可能感興趣的文章

反射方法操作

反射方法操作

「三元運算子」不好嗎?

「三元運算子」不好嗎?

MarkDown語法

MarkDown語法






留言討論