SuperAPI AI 工具集

frontend-architecture

前端与建站

查看项目

更新于 10-16 15:08

技能介绍

Frontend Architecture 是一份关于“如何组织前端代码,使其具备可扩展性”的架构实践指南。其核心主张是关注点分离,而不是按文件类型划分文件夹。该技能适用于 React/Next 与 Vue。资料说明:目录布局应遵循 app-builder,视觉设计参见 frontend-design,React/Next 性能参见 nextjs-react-expert。

一、关注点分离(核心规则) 按职责把代码拆成四层,一个代码单元只承担其中一层职责:UI 层存放渲染、标记、展示状态,位于 components/;逻辑层存放状态、副作用、数据转换、可复用 UI 逻辑,位于 hooks/(React)或 composables/(Vue);数据层存放 API 调用、fetch/axios、缓存键,位于 lib/ 或 service 文件(*.api.ts);类型层存放 TypeScript 类型与领域模型,位于 types.ts / *.types.ts;校验层存放表单/数据 schema,位于 *.schema.ts(zod/yup/valibot)。顶层目录布局遵循项目脚手架技能,该技能关注的是代码属于哪一层,而非文件夹放在哪里。

二、文件职责与体量 每个文件一个清晰职责。体量是信号而非硬性限制:一个职责清晰的 230 行文件,胜过一个只有 90 行却同时负责请求、校验、渲染并处理弹窗与提示的文件。舒适区间参考:UI 组件 80–180 行;页面/屏幕 100–220 行;Hook/composable 40–150 行;API 服务 50–200 行;类型/schema 灵活。当文件混合了 UI + API + 业务逻辑 + 校验 + 状态时应拆分;不要仅为凑行数而拆分一个内聚的文件。

三、组件负责渲染,逻辑放到别处 组件应主要用于渲染,把请求、状态、转换推入 hook 或 composable。反例中组件自身持有数据层,用 useState 与 useEffect 直接 fetch;正例中组件调用 useProducts() 并只负责渲染,在 isLoading 时返回 Loading。自定义 hooks 必须以 use 开头。只有最小的一次性场景,才可接受组件直接调用 API。

四、Next.js:默认使用服务端组件 在 App Router 中,page.tsx 与 layout.tsx 是服务端组件,只有确实需要客户端时才使用 "use client"。服务端组件适合:获取数据、读取数据库/API、处理密钥令牌、渲染静态/半静态布局。客户端组件适合:表单、弹窗、下拉、事件处理、动画、useState/useEffect、浏览器 API(window、localStorage)。保持客户端组件小巧:不要为了一个交互按钮把整页标记为 "use client",而应把按钮抽成独立的客户端组件,让页面保持为服务端组件。

五、Vue:组合式 API + composables 面向完整生产应用,优先使用组合式 API 与 <script setup> 单文件组件(选项式 API 适合简单场景/渐进增强)。components/ 放 UI;composables/ 放可复用纯逻辑(useX);service 文件放 API 调用。可复用纯逻辑用 composable,同时复用逻辑与布局时用组件。

六、状态:从局部开始,按需升级 组件内部状态用 useState/ref;单一功能内的可复用状态/逻辑用自定义 hook/composable;子树内共享 UI 状态用 Context(React)或 provide-inject(Vue);跨应用、复杂、需持久化用 Zustand/Pinia/Redux;服务端状态/API 缓存用 TanStack Query(react-query)或类似方案。小型应用不要在第一天就上全局状态或 Redux;服务端状态应放到查询库,而非全局 store。

七、API 放在服务文件 不要把原始 fetch/axios 散落在组件中。示例中 user.api.ts 导出 getUsers(),封装 http.get('/users') 并返回 res.data;useUsers.ts 中通过 useQuery 使用 queryKey ['users'] 与 queryFn: getUsers。始终显式处理 loading、error、empty 状态。

八、表单基于 schema 校验 不要在组件内联长长的校验逻辑。React/Next 使用 react-hook-form + zod(或 yup);Vue 使用 vee-validate + zod/yup;schema 放在被校验表单旁的 *.schema.ts。

九、命名 描述性而非隐晦。允许借助文件夹提供上下文,但倾向显式。推荐 UserProfileCard.tsx、useCreateBooking.ts、booking.api.ts、booking.schema.ts;避免 Card.tsx、handle.ts、裸 api.ts、data.ts。

十、Props 显式声明 props 类型。当一个组件接收许多相关字段时,传递对象而非散落的原始值。正例为 type ProductCardProps = { product: Product; onSelect?: (p: Product) => void };反例为传入 id、name、price、image、discount、stock 等七个松散 props。

十一、反“上帝组件” 出现以下信号时应拆分组件:超过约 200 行;超过约 3 个 useEffect/watch;大量 useState/ref;同时渲染 UI 和请求 API;大量 if/else 业务分支;一个文件里有多个弹窗/表格/表单。沿页面接缝分解:Page 下包含 Header、Filter、Table/List、Pagination、Modal/Form。

十二、Tailwind 类名卫生 如果 className 超过约 5–8 个逻辑分组,就抽成组件;重复模式抽成可复用组件或变体辅助;不要在 className 中内联复杂条件逻辑,改用 cn()。示例:const cardClassName = cn('rounded-xl border p-4', isActive && 'bg-blue-500 text-white', isError && 'border-red-500')。

十三、TypeScript 开启 strict: true;避免 any;显式标注 props、API 响应与领域模型;当联合类型足够时优先用联合类型而非枚举;用 schema(zod)校验外部数据——仅靠类型无法守护运行时输入。

十四、最小测试 不必一开始测试一切,但应覆盖:工具函数用单元测试;重要 hooks/composables 用单元测试;主要表单用校验测试;关键流程用 e2e。可就近放置(useBooking.test.ts 与 useBooking.ts 同目录),或保留 tests/unit + tests/e2e——选定一种并保持一致。

十五、无障碍底线 操作用 <button> 而非 <div onClick>;每个输入有 label,每张图片有 alt;弹窗支持键盘 escape 与焦点陷阱;表单清晰显示错误。

核心记忆点:一个文件一个职责 · UI 不持有逻辑 · 逻辑进 hook/composable · API 进 service · 校验进 schema · 类型单独存放 · Next 服务端优先 · Vue composable 优先。目录结构来自脚手架技能,而非该技能。

关于收费、价格、平台支持等具体信息,资料暂未提供。