关于 engineering-frontend-developer
面向前端开发者的指南,涵盖 React、Vue、Angular 和 Svelte 应用、组件库及性能优化。它通过具体决策规则指导布局、无障碍、测试、构建工具和渲染方案的选择。
主要功能
- 01React、Vue、Angular 和 Svelte 指南
- 02组件架构与 TypeScript 约定
- 03性能分析与优化决策
- 04无障碍实现和测试规则
- 05构建工具及 SSR、CSR、SSG 选择
适用场景
- 01规划新 Web 应用的框架与构建工具
- 02排查布局、性能或水合问题
- 03构建组件库并检查无障碍要求
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ peterhdd/ agent-skills --skill engineering-frontend-developer安装后在 Claude Code 中输入/engineering-frontend-developer确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/css-patterns.mddata-table.mdreact-patterns.mdtypescript-patterns.mdscripts/check_bundle.sh详细介绍
前端开发指南
概述
本指南涵盖使用 React、Vue、Angular 和 Svelte 进行现代前端开发,包括组件架构、性能优化、无障碍访问和测试。构建 Web 应用、组件库或优化前端性能时,请使用本指南。
框架和布局决策规则
- 选择框架时,应结合团队的专业知识和项目限制;生态系统广泛时默认选择 React;需要渐进式增强现有页面时选择 Vue;对包体积要求严格的应用选择 Svelte;项目需要内置 DI 和路由且约定明确的全功能框架时选择 Angular。
- 实现设计时,使用 CSS Grid 进行二维页面布局,使用 Flexbox 对齐一维组件;避免将绝对定位用于布局,因为这会破坏响应式重排。
- 构建组件库时,每个组件都应作为具名导出提供,并包含 TypeScript props 接口、Storybook story 和单元测试;缺少其中任何一项的组件都不会被合并。
- 与后端 API 集成时,应将 fetch 逻辑集中到有类型的 API 客户端层(例如,使用
fetch或axios和拦截器的单个api.ts模块),以便在同一处处理身份验证标头、错误转换和重试。
性能决策规则
- 如果页面在 Lighthouse CI 中的最大内容绘制耗时超过 2.5 秒,应将其视为阻塞性错误;使用 Chrome DevTools 的 Performance 标签页进行分析,并在合并前修复最大的瓶颈。
- 添加动画时,使用 CSS 的
transform和opacity属性(仅由合成器处理),而不要使用width、height或top/left,以避免触发导致卡顿的布局重新计算。 - 应用需要离线支持时,为静态资源注册采用缓存优先策略的 service worker,为 API 请求注册采用网络优先策略的 service worker,并在离线时回退到缓存响应。
- 初始 JS 包超过预算(例如,压缩后为 200 KB)时,使用
React.lazy()或动态import()添加基于路由的代码拆分,并延迟加载首屏以下的非关键脚本。 - 支持旧版浏览器时,定义 browserslist 配置,并让构建工具(Vite、Webpack)自动添加 polyfill;除非 browserslist 覆盖不足,否则不要手动添加 polyfill 或功能检测。
- 添加图片时,使用
<picture>和 WebP/AVIF 源,并显式设置width/height属性以防止布局偏移;对于首屏以下的图片,添加loading="lazy"。 - 如果某个路由在初始页面加载时不需要,应使用
React.lazy()(或框架的等效功能)和<Suspense>回退将其封装起来,使主包不包含该路由的代码。 - 提供静态资源时,应配置 CDN 或服务,在带内容哈希的文件名上设置
Cache-Control: public, max-age=31536000, immutable,并在index.html上设置no-cache。
无障碍访问决策规则
- 构建任何交互组件时,都应添加 ARIA 属性、键盘处理程序(按需支持
Enter、Space、Escape),并在标记任务完成前使用 axe-core 测试。 - 构建表单时,使用
htmlFor/id将每个<input>与<label>关联,提供通过aria-describedby关联的可见错误消息,并确保仅使用键盘导航也能完整操作表单。 - 使用颜色传达含义(例如,错误状态、状态徽章)时,务必添加辅助指示(图标、文本、图案),以便色盲用户区分状态。
- 添加模态框或下拉菜单时,在其打开期间将焦点限制在元素内,并在关闭时将焦点返回触发元素;通过不使用鼠标而用 Tab 键完成整个流程进行测试。
- 拉取请求新增交互组件时,必须包含 axe-core 集成测试,断言不存在 WCAG 2.1 AA 违规,才能合并。
CSS 调试决策规则
布局出问题时,请根据症状进行诊断:
- Flex 子项溢出其容器:为 Flex 子项添加
min-width: 0(行布局)或min-height: 0(列布局)。Flex 项的默认值为min-width: auto,这会阻止其缩小到内容尺寸以下。 - Grid 项忽略列宽:使用
minmax(0, 1fr),而不是1fr。单独使用1fr意味着minmax(auto, 1fr),会让内容撑大列宽,超出预期。 position: sticky不生效:检查所有祖先元素是否设置了overflow: hidden、overflow: auto或overflow: scroll。这些属性都会创建新的滚动上下文,并将 sticky 元素限制在其中。同时确认元素已设置top/bottom值。- 使用
margin: auto居中的元素没有居中:确认元素设置了明确的width(块级元素),或父元素设置了display: flex(Flex 子项)。对全宽块级元素使用margin: auto不会生效。 z-index不生效:元素需要设置position: relative/absolute/fixed/sticky才能创建堆叠上下文。同时检查祖先元素是否通过transform、opacity < 1、filter或will-change创建了堆叠上下文,从而限制z-index的作用范围。- inline-block/inline 元素之间有间隙:HTML 中的空白字符会产生间隙。修复方法:在父元素上使用
display: flex(首选),或将父元素的font-size设为0,并在子元素上重置。 - 相邻元素之间有 1px 间隙:这是亚像素渲染导致的。修复方法:调试时使用
outline代替border,或为父元素设置background以遮盖间隙。 - 文本省略截断不生效:必须同时设置
overflow: hidden、white-space: nowrap和text-overflow: ellipsis。多行截断时,使用-webkit-line-clamp,并设置display: -webkit-box和-webkit-box-orient: vertical。 - 容器查询未生效:确认祖先元素设置了
container-type: inline-size(或size)。元素自身不能作为自己的容器,查询必须引用父元素。
构建工具决策规则
- Vite(默认):所有新项目都使用 Vite。通过原生 ESM 提供即时开发服务器、快速 HMR 和基于 Rollup 的生产构建。如果项目使用了与 Vite 不兼容的 CommonJS 专用依赖,为这些包添加
optimizeDeps.include,不要切换到 Webpack。 - Webpack:仅用于已经使用 Webpack 的现有项目,或特定的 Webpack 专用插件没有 Vite 等效方案的情况(很少见)。有机会时迁移到 Vite(参见迁移决策规则)。
- Turbopack:仅在 Next.js 14+ 中使用(
next dev --turbo)。目前还不能独立使用。对于 Next.js 项目,其开发构建速度比 Webpack 更快。如果构建已经很快(<5s),就不必切换。 - esbuild:用于库打包、脚本或 CLI 工具,不用于完整应用。不支持 HTML 入口点处理,也不支持按路由拆分代码。发布 npm 包时使用
tsup(esbuild 封装器)。 - SWC 与 Babel:使用 SWC(通过 Vite 插件或 Next.js 默认配置)。SWC 的转换速度快 20-70 倍。仅当项目依赖没有 SWC 等效方案的 Babel 专用插件时才使用 Babel(这种情况越来越少)。
- Monorepo 工具:少于 5 个包时使用 npm/pnpm workspaces,不需要额外工具。5-20 个包时使用 Turborepo 缓存任务。超过 20 个包或涉及多种语言时使用 Nx。单包项目绝不要添加 Monorepo 工具。
SSR 和服务器组件决策规则
何时使用 SSR、CSR 或 SSG
- SSG(静态站点生成):适用于每小时更新不到一次的内容(营销页面、文档、博客文章)。在部署时构建。TTFB 尽可能快。
- SSR(服务器端渲染):适用于个性化内容(仪表板、用户个人资料)、SEO 至关重要且包含动态数据的页面,或不能接受数据过时的页面。每次请求都会增加服务器延迟——使用
Cache-Control或 CDN 积极缓存。 - CSR(客户端渲染):适用于仅限已认证用户访问的页面(管理面板、内部工具),这类页面不需要考虑 SEO,交互性是首要目标。构建和部署最简单(静态托管)。
- ISR(增量静态再生成):适用于大多为静态内容、但需要定期更新的页面(商品列表、价格页面)。根据数据可过时的时长设置
revalidate间隔:价格设为 60s,博客文章设为 3600s。
React Server Components(RSC)决策规则
- 默认使用 Server Components:除非组件需要交互功能,否则都使用 Server Component。如果组件包含
onClick、useState、useEffect或浏览器 API,就必须是 Client Component('use client')。 - 数据获取:直接在 Server Components 中使用
async/await获取数据——不需要useEffect、TanStack Query 或加载状态。HTML 发送之前,数据就已解析完成。 - 在 Server Components 中进行身份验证:在 Server Component 中读取会话/ Cookie,并将用户对象作为 prop 传递给 Client Components。不要在 Client Components 中读取 Cookie 或会话——它们在客户端执行。
- 何时使用 Server Actions,何时使用 API 路由:对于从 UI 调用的表单提交和数据变更,使用 Server Actions(
'use server')。对于 Webhook、第三方集成或由外部服务调用的端点,使用 API 路由(/api/*)。 - 在 Server Components 和 Client Components 之间共享状态:将可序列化数据作为 prop 从 Server 传递给 Client。如果需要基于服务器数据派生客户端状态,请使用服务器 prop 初始化
useState,并在客户端管理更新。 - 大型数据集:使用
<Suspense>边界进行流式传输。将较慢的 Server Component 放在<Suspense fallback={<Skeleton />}>中。页面外壳会立即渲染;较慢的组件准备就绪后再流式传输。将 Suspense 边界放在有意义的 UI 区块(侧边栏、主要内容、评论)上,而不是包裹每个组件。
水合错误决策树
- 错误:“Text content does not match”:服务器渲染和客户端渲染的值不同。常见原因:
- 在渲染时使用
Date.now()、Math.random()或new Date().toLocaleString()→ 移至useEffect+useState。 - 使用
typeof window !== 'undefined'条件渲染 → 使用useEffect设置hasMounted标志。 - 浏览器扩展注入 DOM 节点 → 这不是你的问题,但可以在无痕模式下测试确认。
- 在渲染时使用
- 错误:“Hydration failed because the server rendered HTML didn't match”:结构不同(元素不同)。常见原因:
- 在
<p>中嵌套<p>,或在<p>中嵌套<div>→ 修正嵌套方式;HTML 规范不允许这样做。 - 第三方组件在服务器和客户端的渲染结果不同 → 使用
dynamic(() => import(...), { ssr: false })包裹。 - 缺少结束标签,或 HTML 要求显式闭合的标签使用了自闭合写法。
- 在
性能调试工作流
页面变慢时,请按以下顺序操作——不要跳过步骤:
步骤 1:确定瓶颈类型
- 运行 Lighthouse。检查哪个指标未达标:
- LCP >2.5s:最大的可见元素加载过慢。转到步骤 2。
- FID/INP >200ms:JavaScript 阻塞了用户交互。转到步骤 3。
- CLS >0.1:首次绘制后布局发生偏移。转到步骤 4。
步骤 2:修复 LCP
- 检查 LCP 元素是什么(Lighthouse 会显示)。通常是:首屏主图、标题文本或视频。
- 如果是图片:添加
priority(Next.js)或fetchpriority="high"。明确设置width/height。使用 WebP/AVIF 格式提供图片。如果图片由 CDN 提供,请检查缓存标头。 - 如果是文本:检查是否有阻塞渲染的字体。使用
font-display: swap或font-display: optional。使用<link rel="preload" as="font" crossorigin>预加载关键字体文件。 - 如果被 JS 阻塞:说明代码包太大。请查看步骤 3。创建 LCP 元素的 JS 加载并执行之前,该元素无法渲染。
- 检查服务器响应时间(TTFB)。如果 TTFB >600ms,瓶颈在后端或 CDN,而非前端。
步骤 3:修复 INP(交互到下一次绘制)
- 打开 Chrome DevTools → Performance → 记录一次点击/输入交互。
- 找到长任务(>50ms 的黄色条)。点击它以查看调用堆栈。
- 如果长任务来自你的代码:使用
requestIdleCallback、scheduler.yield()(Chrome 115+)或setTimeout(fn, 0)将工作拆分成更小的块,让出执行权,使浏览器能够在帧之间处理其他任务。 - 如果长任务来自 React 重渲染:打开 React DevTools Profiler。找到触发重渲染的组件。常见修复方法:使用
React.memo(开销较大的子组件)、useMemo(派生值),或将状态移至更靠近其使用位置的地方。如果单次状态更改导致 >500 个组件重渲染,说明状态放得太靠上了。 - 如果长任务来自第三方脚本(分析、广告):使用
async或defer属性延迟加载,或者在requestIdleCallback后加载。
步骤 4:修复 CLS
- 检查哪个元素发生了位移(Lighthouse 会显示,或者使用 DevTools → “Layout Shift Regions”)。
- 没有尺寸的图片/视频:添加
width和height属性(或使用aspect-ratioCSS)。浏览器会在资源加载前预留空间。 - 字体切换:回退字体与 Web 字体的度量指标不同。使用
@font-face中的size-adjust匹配度量指标,或者使用font-display: optional完全消除字体切换。 - 在首屏上方注入动态内容:广告、横幅、Cookie 提示。通过在容器上设置
min-height来预留空间。如果内容高度不固定,请使用contain: layout,避免位移向外传播。 - 延迟加载的 CSS:如果样式表在首次绘制后加载并更改可见布局,请内联关键 CSS,或预加载样式表。
代码质量决策规则
- 编写测试时,要求每个组件至少有一个覆盖其主要渲染路径的单元测试,以及一个交互测试(点击、键盘)——通过 CI 覆盖率门槛强制要求行覆盖率至少达到 80%。
- 启动项目时,第一天就在
tsconfig.json中启用strict: true;随着代码库增长,之后再补上严格模式会变得越来越困难。 - API 调用或异步操作失败时,显示面向用户的错误消息并提供重试操作——绝不静默吞掉错误或显示原始异常文本。
- 组件文件超过 300 行时,将其拆分为更小的子组件,并使用共享的 barrel export;大型文件通常意味着职责混杂。
- 设置 CI 时,将 lint(ESLint)、类型检查(
tsc --noEmit)、测试(Vitest/Jest)和代码包大小检查设为必需门槛——合并前这四项必须全部通过。
工作流
步骤 1:项目设置与架构
- 使用合适的工具配置现代开发环境。
- 配置构建优化和性能监控。
- 建立测试框架并集成 CI/CD。
- 创建组件架构和设计系统基础。
步骤 2:组件开发
- 创建类型完善、可复用的组件库。
- 采用移动优先的方法实现响应式设计。
- 从一开始就在组件中融入无障碍功能。
- 为所有组件编写全面的单元测试。
步骤 3:性能优化
- 实施代码拆分和延迟加载策略。
- 优化图片和资源,以便在 Web 上交付。
- 监控 Core Web Vitals 并据此进行优化。
- 设置性能预算和监控。
步骤 4:测试和质量保证
- 编写全面的单元测试和集成测试。
- 使用真实的辅助技术进行无障碍测试。
- 测试跨浏览器兼容性和响应式行为。
- 为关键用户流程实施端到端测试。
参考
Lighthouse CI 目标
- 性能得分:90+
- 所有交互元素均可通过键盘导航,并显示焦点指示器
- axe-core:符合 WCAG 2.1 AA 级别,违规数为零
- 包大小:初始 JS 经 gzip 压缩后 < 200 KB(使用
bundlesize或同等工具强制执行) - 生产代码中不使用 TypeScript
any类型断言;在 tsconfig 中启用strict: true
迁移决策规则
- CRA 迁移到 Vite:当 CRA 的构建时间超过 30s 或弃用 CRA 的压力增大时进行迁移。步骤:将
react-scripts替换为vite+@vitejs/plugin-react,将index.html移至根目录,将process.env更新为import.meta.env,修复所有 CommonJS 导入。预算:小型应用需 2-4 小时,大型应用(使用自定义 Webpack 配置)需 1-2 天。 - Pages Router 迁移到 App Router(Next.js):增量迁移。每次将一个路由移至
app/目录。从静态/简单页面开始,将复杂页面(包含 getServerSideProps + 复杂状态)留到最后。不要一次性迁移整个应用。预算:复杂应用每天迁移 1 个路由。 - JavaScript 迁移到 TypeScript:在 tsconfig 中启用
allowJs: true,然后逐个将文件从.js重命名为.tsx。从叶子组件(无依赖项)开始,逐步向内推进。从第一天起就在新文件中强制启用strict: true;如有需要,可创建一个扩展基础配置的tsconfig.strict.json。预算:每天约 10 个文件。 - CSS Modules 迁移到 Tailwind:不要一次性重写所有样式。在安装 Tailwind 的同时保留 CSS Modules。新组件使用 Tailwind;只有在因其他原因修改旧组件时,才迁移这些组件。预算:在日常开发中逐个组件迁移。
- 类组件迁移到 hooks:仅在因功能变更需要修改组件时才迁移。不要迁移稳定且未修改的类组件——它们运行良好。迁移时:
componentDidMount→useEffect(..., []),componentDidUpdate→ 带依赖项的useEffect,this.state→useState。
状态管理决策矩阵
- 共享状态的组件少于 5 个:通过 props 向上提升状态。无需使用库。如果 props 逐层传递超过 3 层,则为该特定状态切片添加 React Context。
- 共享状态的组件为 5-15 个:对于结构化状态,使用 React Context +
useReducer;对于更简单的 API,使用 Zustand。如果状态是服务端数据,则使用 TanStack Query,而不是 Context。 - 组件超过 15 个或派生状态复杂:使用 Zustand(简单、包体积小)或 Jotai(原子化、自底向上)。只有团队已熟悉 Redux 时才使用 Redux Toolkit。切勿在新项目中引入 Redux。
- 服务端状态(API 数据、缓存、同步):使用 TanStack Query(React)、SWR(需求较简单)或 Apollo Client(GraphQL)。切勿将服务端数据存储在客户端状态管理器(Zustand、Redux)中——这会导致数据过期和重复的缓存管理。
- 表单状态:复杂表单(字段超过 5 个、需要验证或包含动态字段)使用 react-hook-form。简单表单(字段少于 5 个、无复杂验证)使用原生
useState。切勿使用全局状态管理器管理表单状态。 - URL 状态(筛选、分页、搜索):使用 URL 搜索参数(
useSearchParams)作为唯一数据源。仅对派生值同步到组件状态。这样状态便于分享、添加书签,并支持浏览器返回按钮。
自我验证协议
完成前端实现后,请进行验证:
- 运行 Lighthouse CI,并确认:Performance >=90、Accessibility >=90、Best Practices >=90。如果任何分数低于 90,请在合并前修复。
- 对每个新增页面/组件运行
npx axe-core或axe-playwright。确保没有 WCAG 2.1 AA 违规。 - 测试键盘导航:使用 Tab 键遍历整个页面。每个交互元素都必须可访问且可操作。焦点指示器必须清晰可见。
- 打开浏览器 DevTools 的 Performance 面板,记录一次用户交互,并检查是否存在:布局抖动(强制重排)、超过 50ms 的长任务、过多的重新渲染。
- 检查打包体积:运行
npx bundlesize或查看构建输出。如果初始 JS 的 gzip 压缩后体积超过 200KB,请检查打包内容(使用npx source-map-explorer)。 - 在 320px、768px 和 1280px 的视口宽度下测试响应式布局。不得出现水平滚动、元素重叠或文本无法阅读的情况。
- 将浏览器 DevTools 的网络限速设为 "Slow 3G" 并进行测试。如果页面无法正常使用,请添加加载状态并优化关键渲染路径。
故障恢复
- 组件重新渲染过于频繁:使用 React DevTools Profiler 找出原因。常见修复方法:使用
React.memo进行记忆化(适用于开销较大的子组件),使用useMemo/useCallback记忆化传递给 props 的派生值/回调,或将状态移至更接近其使用位置的地方。 - 打包体积突然增大:运行
source-map-explorer找出原因。常见原因:导入整个库(import _ from 'lodash'→import groupBy from 'lodash/groupBy')、意外打包了开发依赖,或引入了体积较大的 polyfill。 - Hydration 不匹配(SSR/SSG):服务端渲染的 HTML 与客户端渲染结果不同。常见原因:在渲染过程中使用
Date.now()或Math.random()、在 SSR 期间访问window/document,或根据仅客户端状态进行条件渲染。修复方法:使用useEffect处理仅客户端的值,或将suppressHydrationWarning作为最后手段。 - CSS 布局在特定视口下错乱:检查是否存在:硬编码的像素宽度、Flex 子项缺少
min-width: 0、overflow: hidden裁剪内容,或position: absolute元素脱离其容器。使用带minmax()的 CSS Grid 代替固定宽度。 - API 调用过于频繁:检查
useEffect依赖数组是否缺失或不正确。如果使用 TanStack Query,请设置staleTime,避免每次挂载时都重新获取数据。如果使用useEffect获取数据,可考虑改用数据获取库。
熟悉现有代码库
加入现有前端项目时:
- 运行
npm start/npm run dev(5 分钟)——如果运行失败,请先修复开发环境。 - 检查技术栈(5 分钟)——框架(React/Vue/Angular/Svelte)、打包工具(Vite/Webpack/Turbopack)、CSS 方案(Tailwind/CSS Modules/styled-components)、状态管理和测试库。
- 运行测试套件(5 分钟)——运行
npm test。记录覆盖率、测试类型以及未覆盖的流程。 - 打开浏览器 DevTools(10 分钟)——检查打包体积(Network 面板)、控制台错误和 Lighthouse 分数。这些是基准数据。
- 阅读组件树(10 分钟)——从应用根节点开始。梳理顶层路由及其关键组件。找出共享组件(设计系统、布局)。
- 检查技术债迹象(5 分钟)——是否有
any类型断言?eslint-disable注释?超过 500 行的文件?内联样式?这些都表明相关区域较脆弱。 - 梳理数据流(10 分钟)——数据如何从 API 到达 UI?是否有集中式 API 客户端?是否有加载/错误状态?服务端状态是通过库还是手动管理的?
脚本
scripts/check_bundle.sh—— 分析构建输出目录中的 JS 和 CSS 包大小,并估算 gzip 压缩后的大小,支持配置警告和错误阈值。运行时使用--help查看选项。
请参阅DataTable 组件,了解使用 React + TypeScript 实现的完整虚拟化 DataTable。 请参阅React 组件模式,了解复合组件、自定义钩子、错误边界、乐观 UI 和表单验证模式。 请参阅TypeScript 模式,了解可辨识联合、多态组件、品牌类型和类型安全的 API 客户端。 请参阅CSS 模式,了解现代 CSS Grid 布局、容器查询、主题设置、视图过渡和 :has() 选择器。
评论
支持 Markdown