前端开发指南 是做什么的?
概述
本指南涵盖了使用 React、Vue、Angular 和 Svelte 的现代前端开发,包括组件架构、性能优化、可访问性和测试。在构建 Web 应用程序、组件库或优化前端性能时使用。
框架与布局决策规则
- 选择框架时,将其与团队专业知识和项目约束相匹配;在需要广泛生态系统时默认使用 React,在需要渐进式增强现有页面时使用 Vue,在需要包体积关键型应用时使用 Svelte,在项目需要内置 DI 和路由的固执己见的完整框架时使用 Angular。
- 实现设计时,使用 CSS Grid 进行二维页面布局,使用 Flexbox 进行一维组件对齐;避免使用绝对定位进行布局,因为它会破坏响应式重排。
- 构建组件库时,将每个组件作为带有 TypeScript props 接口的命名导出、一个 Storybook 故事和一个单元测试暴露出来——三个条件缺一不可的组件不会被合并。
- 与后端 API 集成时,将 fetch 逻辑集中到一个类型化的 API 客户端层(例如,一个使用
fetch或axios并带有拦截器的单一api.ts模块),以便认证头、错误转换和重试都在一处处理。
性能决策规则
- 当页面在 Lighthouse CI 中的最大内容绘制时间超过 2.5 秒时,将其视为阻塞性错误——使用 Chrome DevTools Performance 选项卡进行性能分析,并在合并前修复最大的瓶颈。
- 添加动画时,使用 CSS
transform和opacity属性(仅合成器属性),而不是width、height或top/left,以避免触发导致卡顿的布局重新计算。 - 当应用需要离线支持时,注册一个 Service Worker,对静态资源采用缓存优先策略,对 API 请求采用网络优先策略,并在离线时回退到缓存响应。
- 当初始 JS 包超过预算(例如,200 KB gzipped)时,使用
React.lazy()或动态import()添加基于路由的代码拆分,并将非关键脚本推迟到首屏内容下方。 - 支持旧浏览器时,定义一个 browserslist 配置,让构建工具(Vite、Webpack)自动填充 polyfill;除非 browserslist 覆盖范围不足,否则不要手动添加 polyfill 或特性检查。
- 添加图片时,使用包含 WebP/AVIF 资源以及显式
width/height属性的<picture>元素,以防止布局偏移;对于首屏下方的图片,添加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 键遍历整个流程进行测试。
- 当拉取请求添加新的交互组件时,该 PR 必须包含一个 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。其中任何一个都会创建一个包含粘性元素的新滚动上下文。还要验证元素是否设置了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 的范围。- 内联块/内联元素之间的间隙: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(默认):用于所有新项目。通过原生 ESM 实现即时开发服务器、快速 HMR、基于 Rollup 的生产构建。如果项目使用的 CommonJS-only 依赖在 Vite 中失败,请为这些包添加
optimizeDeps.include——不要切换到 Webpack。 - Webpack:仅用于已经使用它的现有项目,或者当特定的 Webpack-only 插件没有 Vite 等效替代时(很少见)。在有机会时迁移到 Vite(参见迁移决策规则)。
- Turbopack:仅用于 Next.js 14+ 内部(
next dev --turbo)。尚不能独立使用。为 Next.js 项目提供比 Webpack 更快的开发构建。如果构建已经很快(<5 秒),则无需切换。 - esbuild:用于库打包、脚本或 CLI 工具——不用于完整应用程序。没有 HTML 入口点处理,没有按路由的代码拆分。使用
tsup(esbuild 封装器)发布 npm 包。 - SWC 对比 Babel:使用 SWC(通过 Vite 插件或 Next.js 默认)。SWC 在转换方面快 20-70 倍。仅在项目依赖于没有 SWC 等效替代的 Babel-only 插件时使用 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间隔:价格设为 60 秒,博客文章设为 3600 秒。
React 服务器组件决策规则
- 默认使用服务器组件:每个组件都是服务器组件,除非它需要交互性。如果组件具有
onClick、useState、useEffect或浏览器 API——则它必须是客户端组件('use client')。 - 数据获取:直接在服务器组件中使用
async/await获取数据——不需要useEffect、TanStack Query 或加载状态。数据在 HTML 发送之前已解析。 - 服务器组件中的认证:在服务器组件中读取会话/cookie,并将用户对象作为 prop 传递给客户端组件。永远不要在客户端组件中读取 cookie 或会话——它们在客户端执行。
- 何时使用服务器操作与 API 路由:对从 UI 调用的表单提交和变更使用服务器操作(
'use server')。对 Webhook、第三方集成或外部服务调用的端点使用 API 路由(/api/*)。 - 在服务器和客户端组件之间共享状态:将可序列化的数据作为 props 从服务器传递到客户端。如果需要从服务器数据派生的客户端状态,使用服务器 prop 初始化
useState并在客户端管理更新。 - 大数据集:使用
<Suspense>边界进行流式传输。将慢速服务器组件包裹在<Suspense fallback={<Skeleton />}>中。外壳立即渲染;慢速组件在准备好时流式传入。将 Suspense 边界放置在有意义的 UI 部分(侧边栏、主要内容、评论)——而不是每个组件周围。
水合错误决策树
- 错误:“文本内容不匹配”:一个值在服务器和客户端渲染之间不同。常见原因:
- 在渲染中使用
Date.now()、Math.random()或new Date().toLocaleString()-> 移动到useEffect+useState。 typeof window !== 'undefined'条件渲染 -> 使用useEffect设置hasMounted标志。- 浏览器扩展注入 DOM 节点 -> 不是你的 bug,但在无痕模式下测试以确认。
- 在渲染中使用
- 错误:“水合失败,因为服务器渲染的 HTML 不匹配”:结构差异(不同的元素)。常见原因:
<p>嵌套在<p>内,或<div>嵌套在<p>内 -> 修复嵌套,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 -> "布局偏移区域")。
- 没有尺寸的图片/视频:添加
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 调用或异步操作失败时,显示面向用户的错误消息并提供重试操作——永远不要无声地吞没错误或显示原始异常文本
…
[为翻译而截断]