重构第13周笔记:项目结构化(逻辑/组件/组装三层分离)
路线图阶段:第三阶段(Design Engineer 工具箱)第13周 —— 正经重构 任务:把挤在 App.tsx 里的所有东西,拆成”类型层 / 逻辑层 / 组件层 / 组装层”的标准结构 核心收获:相对路径(
../vs./)、import/export 判断法的主动运用、隔离环境的资源加载
0. 为什么重构:一个文件装了三类东西
重构前 App.tsx 把三类完全不同的东西塞在一起:
App.tsx(什么都有)
├── 类型定义 RGB / HSL / Palette
├── 颜色逻辑层 9 个纯函数(hexToRgb...generatePalette)
└── UI 组件层 ColorPicker / ColorInput / Swatch / PreviewCard / ColorPalette / App
能跑,但不是组件库该有的样子。week6 立的”逻辑/UI 分离”原则,之前只在概念上分离,这周在物理文件上真正兑现。
1. 目标结构:一个组件一个文件
src/
├── types.ts 类型层(RGB / HSL / Palette)
├── colors.ts 逻辑层(9 个纯函数)
├── components/
│ ├── ColorPicker.tsx ┐
│ ├── ColorInput.tsx │ UI 层:一个组件一个文件
│ ├── Swatch.tsx │
│ └── PreviewCard.tsx ┘
├── App.tsx 组装层(ColorPalette + App)
└── *.stories.tsx story 文件
类比设计:像把 Figma 文件从”一个页面塞所有东西”整理成”Tokens 页 / Components 页 / 产品页”。
colors.ts= token 逻辑层components/= 组件库App.tsx= 产品页(只组装,不定义)
2. ⭐ 重构铁律:从底层往上、小步走、每步都跑
最危险的是”一口气全改完然后跑不起来,不知道哪错了”。所以:
顺序:从”没有依赖的”开始,往上层走
第1步:types.ts (类型,谁都不依赖,最底层)
第2步:colors.ts (依赖 types)
第3步:components/*.tsx (依赖 types + colors)
第4步:App.tsx 清理 (依赖上面全部)
第5步:story 归位
为什么从底层开始:底层不依赖别的,抽出来最干净;上层依赖底层,等底层就位了,上层 import 才有地方指。
每完成一步就 npm run dev —— 报错范围锁定在刚改的那一层
3. ⭐ import / export 判断法(本周反复主动用)
别的文件要写出这个名字 → 要 import → 源头要 export;没写出 → 不用管。 (值和类型同一套规则)
实战中的几次判断:
colors.ts用到RGB/HSL/Palette(写在函数签名里)→ 要 import + types.ts 要 export。App.tsx没写RGB/HSL/Palette任何一个(全靠推断)→ 那行import type {...}是多余的,删掉。TS 不报错(顶多 ESLint 黄线),但留着没用的 import 会让读代码的人困惑”App 为什么要这些类型?”。重构收尾要清理。
import type vs 普通 import
import type { HSL } from "../types"—— 导入的是类型(运行时不存在),加 type 更规范。import { hslToCss } from "../colors"—— 导入的是函数(运行时要调用的值),普通 import。- 判断:导入的是类型还是值?类型 →
import type;函数/组件/值 → 普通 import。
4. ⭐ 同文件内函数互调:不用 import
// colors.ts 内部
function hexToHsl(hex: string): HSL {
return rgbToHsl(hexToRgb(hex)); // 调了同文件的两个函数,不用 import
}
import 只用于”跨文件”拿东西。同一个文件里的函数本来就互相看得见(同一作用域)。 所以九个颜色函数整体搬到 colors.ts,它们之间的调用关系原样保留,一行 import 都不用加。只有跨文件依赖(用 types.ts 的类型)才 import。
5. ⭐⭐ 相对路径:../ vs ./(搬进子文件夹最容易栽的坑)
组件搬进 src/components/ 后,路径前缀全变了:
src/
├── types.ts
├── colors.ts
├── App.tsx
└── components/
└── Swatch.tsx
| 谁 import 谁 | 路径 | 解释 |
|---|---|---|
Swatch.tsx → colors.ts | "../colors" | 从 components/ 出去到 src/ → ../ |
Swatch.tsx → types.ts | "../types" | 同上 |
App.tsx → Swatch.tsx | "./components/Swatch" | 从 src/ 进入 components/ → ./xxx/ |
记忆:
./= 当前目录(同层)../= 上一层目录(出去)./子文件夹/= 进入子文件夹两个方向别搞反:子文件夹里的组件回头拿 colors 用
../(出去);App 进子文件夹拿组件用./components/(进去)。
6. props interface 跟着组件走
SwatchProps 这种”某组件专属的 props 类型” → 搬到该组件文件里,放组件上面。
// Swatch.tsx
import type { HSL } from "../types";
import { hslToCss, hslToHex, textColorFor } from "../colors";
interface SwatchProps { // 跟着 Swatch 搬过来,放组件上面
role: string;
color: HSL;
isCopied: boolean;
onCopy: (role: string, hex: string) => void;
}
export function Swatch({ role, color, isCopied, onCopy }: SwatchProps) { ... }
- 组件库惯例:组件的 props interface 和组件住同一个文件。
SwatchProps一般不用 export(只有自己用),除非别的文件直接写了这个名字。- 通用类型(HSL/Palette)放 types.ts;组件专属类型(SwatchProps)放组件文件。
7. ⭐ 隔离环境的资源加载:Storybook 样式丢失
重构后 Storybook 样式全没了 —— week12 预告过、这次踩到的坑。
根因:Storybook 是独立环境,不走 App.tsx。
- app 靠
App.tsx顶部import "./index.css"吃到 Tailwind。 - Storybook 不经过 App.tsx → 吃不到 → 样式全丢。
解法:在 Storybook 自己的入口 .storybook/preview.ts 单独喂一次:
import "../src/index.css";
// ↑ preview.ts 在 .storybook/,index.css 在 src/ → 出去再进 src
改完重启 Storybook(preview.ts 改动热更新可能不生效)。
举一反三的排查直觉:凡是”app 正常、Storybook 不对”的问题,第一反应 —— 这个东西 Storybook 的入口(preview.ts)加载了吗?(CSS、字体、全局 context、主题 provider 都是这套路。)
8. 作品集卫生:收尾要清干净
重构收尾清理掉:
- 没用到的 import(那行多余的
import type {RGB,HSL,Palette})。 - debug 用的
console.log(colorsArray)。 - 注释掉的废代码。
不是功能问题,是作品集卫生。面试官翻代码看到 console.log / 没用的 import / 死代码,会觉得”没收尾干净”。 和 week6 “README 不造假”、week12 “仓库卫生”是同一类:作品集项目的每个细节都在替你说话。
第13周验收
- 把 App.tsx 拆成 types / colors / components / App 四层
- 理解重构顺序:从底层(无依赖)往上,小步走,每步跑
- 主动运用 import/export 判断法(含删掉多余 import)
- 分清
import type(类型) vs 普通 import(值) - 懂同文件内函数互调不用 import
- ⭐ 掌握相对路径
../(出子文件夹) vs./(进子文件夹) - props interface 跟组件走,通用类型放 types.ts
- ⭐ 修复 Storybook 样式丢失(preview.ts 加载 index.css)
- 作品集卫生:清掉 console.log / 没用的 import
反复提醒过、需继续巩固的点
- 相对路径方向别搞反:出子文件夹
../,进子文件夹./xxx/。 - import/export 判断法:文件里写出名字才 import,源头才 export;没写出就删。
- “app 正常但隔离环境(Storybook)不对” → 查隔离环境的入口加载了没。
下一步(第14周预告)—— 仍在第三阶段
- 项目结构现在干净了,适合往组件库方向继续推进。
- 路线图后续:design tokens 系统化、基础无障碍(a11y)深入、Framer Motion 动画。
- 阶段大作业:小而精致的组件库(按钮/输入框/卡片/弹窗)。
- ⚠️ 结构是地基,现在地基稳了。继续按路线图节奏,别贪多。