Chipmunk & Panda

-- 鼠熊部落格

All work and no play makes Jack a dull boy.

前端项目文件结构规范设计原则

0 结构示例

并非最佳实践,实际文件结构设计应根据项目需要自行调整。
例如,本结构将路由设计为“目录-子目录-页面”的结构,故会存在 ExampleMenu/ExampleSubMenu/ExamplePage 的结构,而实际若不需要则可以选择 pages/ExamplePage 的结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
.
└── src
├── assets
├── common
│ ├── apis
│ │ ├── ExampleApi
│ │ │ ├── types.d.ts
│ │ │ └── index.ts
│ │ └── AnotherExampleApi
│ ├── hooks
│ └── utils
├── components
│ ├── ExampleComponent
│ │ ├── SubComponent
│ │ │ ├── types.d.ts
│ │ │ ├── index.scss
│ │ │ └── index.tsx
│ │ ├── AnotherSubComponent
│ │ │ ├── utils.ts
│ │ │ ├── types.d.ts
│ │ │ ├── index.scss
│ │ │ └── index.tsx
│ │ ├── types.d.ts
│ │ ├── index.scss
│ │ └── index.tsx
│ └── AnotherExampleComponent
│ ├── utils.ts
│ ├── types.d.ts
│ ├── index.scss
│ └── index.tsx
├── pages
│ ├── ExampleMenu
│ │ ├── ExampleSubMenu
│ │ │ ├── ExamplePage
│ │ │ │ ├── api
│ │ │ │ │ ├── types.d.ts
│ │ │ │ │ └── index.ts
│ │ │ │ └── components
│ │ │ │ ├── ExampleComponent
│ │ │ │ │ ├── SubComponent
│ │ │ │ │ │ ├── types.d.ts
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ └── AnotherSubComponent
│ │ │ │ │ │ ├── types.d.ts
│ │ │ │ │ │ └── index.tsx
│ │ │ │ │ ├── types.d.ts
│ │ │ │ │ └── index.tsx
│ │ │ │ └── AnotherExampleComponent
│ │ │ │ ├── types.d.ts
│ │ │ │ └── index.tsx
│ │ │ ├── utils.ts
│ │ │ ├── types.d.ts
│ │ │ ├── index.scss
│ │ │ └── index.tsx
│ │ └── AnotherExampleSubMenu
│ │ └── AnotherExamplePage
│ │ ├── apis
│ │ │ ├── ExampleApi
│ │ │ │ ├── types.d.ts
│ │ │ │ └── index.ts
│ │ │ └── AnotherExampleApi
│ │ ├── utils.ts
│ │ ├── types.d.ts
│ │ ├── index.scss
│ │ └── index.tsx
├── router
│ ├── ExampleRoute
│ │ ├── ModuleLoader.tsx
│ │ └── RouterConfig.ts
│ └── index.tsx
├── store
│ ├── expampleStore.ts
│ ├── anotherExampleStore.ts
│ └── index.ts
└── types
├── error.d.ts
├── event.d.ts
└── index.d.ts

1 分形原则

分形原则:父子目录结构尽量保持一致。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.
└── src
├── components
└── pages
├── AnotherExamplePage
│ └── ...
└── ExamplePage
├── AnotherSubPage
│ └── ...
├── SubPage
│ ├── components
│ │ └── ...
│ ├── constants.ts
│ ├── types.d.ts
│ ├── index.scss
│ └── index.tsx
├── components
│ └── ExampleComponent
│ └── ...
└── index.tsx

ExamplePage 与 SubPage 结构均是 index.tsx + components。

2. 层级一致原则

层级一致原则:引用、依赖、逻辑等关系层级应与目录层级相对应,不同关系层级的文件应尽量不出现在同一目录层级,尽量确保目录结构能直观体现出文件之间的关系(就像思维导图)。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
.
└── src
└── components
├── BadComponent
│ ├── SubComponentA.tsx
│ ├── SubComponentB.tsx
│ └── index.tsx
└── GoodComponent
├── SubComponentA
│ └── index.tsx
├── SubComponentB
│ └── index.tsx
└── index.tsx

虽然在上述文件结构中,GoodComponent 看起来与 BadComponent 相比可能没有明显优势,甚至目录结构更加复杂冗余,但当子组件数量很多或者存在 .scss 等文件时,GoodComponent 就可以很好地将各个文件约束在适当的范围内。

需要注意,目录层级不应是无限度的深,无论是页面还是组件,都应最多只出现父子关系,避免过分套娃,否则反而会增加维护难度。

3. 就近原则

就近原则:对于 api、utils 等文件,放置位置应与使用到的地方尽可能近。例如,如果一个 api 仅有一个组件用到,则应将其与该组件放到一起,而不是放到浅层级目录。例如:

1
2
3
4
5
6
7
8
9
10
11
.
└── src
└── pages
└── ExamplePage
├── SubPage
│ └── ...
├── components
│ └── ...
├── api.ts
├── utils.ts
└── index.tsx

需要注意的是,就近原则不是绝对的近。对于 api 和 utils,即使存在多个子组件,一个父组件总共会需要的 api 或 utils 总量通常也不会太多,因此将其统一放在父组件同级目录会更加便于管理维护,没必要在各个子组件目录下分别进行维护(在尽量不增加文件数的前提下尽可能减少依赖文件之间的目录层级跨度,二者需要权衡)。