- 前端
- 教程
【免费下载链接】preguntas-entrevista-react
Preguntas típicas sobre React para entrevistas de trabajo ⚛️
本文基于 preguntas-entrevista-react 仓库内.agents/skills/vercel-react-best-practices技能库中的 rendering-script-defer-async.md 规则展开。该规则是 Vercel React 最佳实践中"渲染性能(Rendering Performance)"章节(Section 6)下的一条 HIGH 影响级别规则,面向使用 React 与 Next.js 的开发者与 AI Agent,核心解决一个常见但代价高昂的问题:裸<script>标签如何阻塞 HTML 解析,从而推迟首屏内容绘制(FCP)与可交互时间(TTI)。读完本文,你将掌握 defer 与 async 的语义差异与选择标准、如何写出非阻塞的脚本引入代码,以及 Next.js 中next/script的strategy替代方案,并理解本仓库实际工程中(Astro + React 混合架构)对"哪些脚本必须同步、哪些可以延后"的取舍逻辑。
为什么无属性脚本标签会导致渲染阻塞:问题的根源
规则文件 rendering-script-defer-async.md 开篇即点明:不带defer或async的脚本标签会在脚本下载和执行期间阻塞 HTML 解析。这是浏览器 HTML 解析器的一个经典行为:当解析器在<head>或<body>中遇到一个没有异步属性的经典<script src="...">时,会暂停后续 DOM 的构建,先同步下载并执行该脚本,然后才继续解析剩余 HTML。
这一停顿带来两个直接后果:
- 延迟 First Contentful Paint(FCP):HTML 解析停滞意味着首屏可绘制内容迟迟无法提交给渲染管线,用户看到第一帧有意义内容的时间被推迟;
- 延迟 Time to Interactive(TTI):脚本执行本身占用主线程,同时阻塞的 DOM 构建也让交互处理器无法尽早挂载就绪。
在本仓库的规则体系中,这条规则被标记为Impact: HIGH,impactDescription 明确写着eliminates render-blocking。与_sections.md中 Section 6 的定位一致——"Optimizing the rendering process reduces the work the browser needs to do"(优化渲染过程,减少浏览器需要做的工作)。值得对比的是,同一技能库中CRITICAL级别规则(如消除瀑布流、减小包体积)带来的收益量级更大,但脚本阻塞是成本极低、收益立竿见影的高频优化点,几乎任何页面都适用。
defer 与 async:两种并行下载,两种执行语义
规则文件给出了二者最关键的区别,这里结合浏览器规范展开说明:
| 属性 | 下载时机 | 执行时机 | 执行顺序保证 |
|---|---|---|---|
defer | 与 HTML 解析并行 | HTML 解析完成后执行(在DOMContentLoaded触发前) | ✅ 按文档中出现顺序依次执行 |
async | 与 HTML 解析并行 | 下载完成后立即执行,不等待解析结束 | ❌ 无顺序保证,谁先下载完谁先执行 |
| (无属性) | 阻塞式同步下载 | 下载并解析 HTML 的同一刻执行 | 按顺序,但阻塞渲染 |
由此推导出规则给出的选择标准:
- 用
defer:脚本依赖 DOM(需要操作文档结构)或依赖其他脚本(有执行顺序要求)。因为defer保证在 HTML 解析完成后、按声明顺序执行,DOM 必然已就绪,脚本间的依赖关系也能得到满足; - 用
async:脚本彼此独立、不依赖 DOM、不依赖页面内容,典型如第三方统计/分析脚本(analytics)。这类脚本"谁先加载完谁先跑"完全可接受,且不与页面主流程竞争执行时机。
需要特别强调的是:defer只对带src的外部脚本有效,对内联脚本无意义;而async即使下载完成也不会阻塞解析,但执行瞬间仍会占用主线程。理解这一点,才能在设计脚本加载策略时做出正确判断。
反例与正例:从阻塞到非阻塞的改造
规则文件给出了可直接对照的两段代码。先看错误写法(阻塞渲染):
export default function Document() { return ( <html> <head> <script src="https://example.com/analytics.js" /> <script src="/scripts/utils.js" /> </head> <body>{/* content */}</body> </html> ) }这里的两个<script>都没有defer或async,浏览器会依次同步下载并执行它们,<head>之后的 HTML 解析全程被挂起——哪怕两个脚本体积很小,也会在网络往返上额外增加一段可见的首屏延迟。
再看正确写法(非阻塞):
export default function Document() { return ( <html> <head> {/* 独立脚本 - 用 async */} <script src="https://example.com/analytics.js" async /> {/* 依赖 DOM 的脚本 - 用 defer */} <script src="/scripts/utils.js" defer /> </head> <body>{/* content */}</body> </html> ) }改造逻辑非常清晰:analytics.js属于独立的第三方埋点脚本,使用async让它在下载完成后自行执行,不干扰页面解析;utils.js大概率需要操作 DOM(如增强交互、初始化组件),使用defer保证它在解析完成后、按声明顺序执行。两条脚本的下载都与 HTML 解析并行进行,首屏路径上不再有任何同步脚本。
Next.js 场景:优先使用 next/script 的 strategy 属性
规则文件在代码示例之后补充了一条重要 Note:在 Next.js 中,优先使用next/script组件配合strategy属性,而不是裸写<script>标签。原因在于next/script把脚本加载决策提升为框架级能力,自动处理优先级、去重、位置与加载时机:
import Script from 'next/script' export default function Page() { return ( <> <Script src="https://example.com/analytics.js" strategy="afterInteractive" /> <Script src="/scripts/utils.js" strategy="beforeInteractive" /> </> ) }strategy的可选值与原生属性的对应关系(本仓库.agents/skills/next-best-practices/scripts.md有更完整的展开):
beforeInteractive:在页面可交互之前加载并执行(内部实现类似defer的高优先级变体),仅用于关键脚本,且只能放在根布局或_document中;afterInteractive(默认):页面变为可交互后再加载,适合埋点、分析等非关键脚本,对应"用async处理独立脚本"的思路;lazyOnload:浏览器空闲时段加载,适合低优先级组件脚本;worker(实验性):在 Web Worker 中执行,彻底移出主线程。
这与原生defer/async的取舍一一对应:afterInteractive与lazyOnload承担了"独立、非关键脚本"的角色(原生async的职责),beforeInteractive则承担"页面交互必需、但必须保证顺序"的角色(原生defer的职责,并进一步提前)。规则的底层目标始终一致:让下载并行化、执行错峰化,避免任何脚本阻塞首屏渲染。
规则库的工程化组织:一条规则如何进入最佳实践体系
这条规则并非孤立文档,它只是 Vercel React Best Practices 技能库 40+ 条规则之一,理解它所在的组织结构有助于你在仓库中定位同类规则、评估优化优先级:
- 规则文件命名即分类:文件以
rendering-为前缀(rendering-script-defer-async.md),按 _sections.md 的约定归入 Section 6 "Rendering Performance"(影响等级 MEDIUM,描述为"优化渲染过程以减少浏览器工作量");其余前缀如async-(消除瀑布流,CRITICAL)、bundle-(包体积优化,CRITICAL)、server-、client-、rerender-、js-、advanced-各自对应不同章节; - 统一模板保证一致性:_template.md 规定了每条规则的 frontmatter(
title、impact、impactDescription、tags)与"反例/正例 + 解释"的正文结构,这正是本规则可读性高的原因;frontmatter 中的tags: rendering, script, defer, async, performance让 Agent 与搜索引擎都能快速索引; - 编译产物 AGENTS.md:规则通过
pnpm build汇总进 AGENTS.md,在编译版中本条规则编号为6.8 Use defer or async on Script Tags(见 AGENTS.md 目录与正文第 2546-2591 行),按标题字母序自动排序,ID 由构建脚本生成; - 影响等级体系:技能库 README 定义了从 CRITICAL 到 LOW 的六级影响评估,HIGH 意味着"显著的性能提升"。在 AGENTS.md 的抽象说明中,这套体系用于"指导自动化重构与代码生成",即 AI Agent 在维护、生成或重构 React/Next.js 代码库时会优先遵循 HIGH/CRITICAL 级别的规则。
从仓库实战看脚本加载决策:哪些脚本可以"特殊豁免"
任何规则都有边界。本仓库(preguntas-entrevista-react,一个 Astro + React 混合架构的静态站点)的实际代码展示了"非阻塞"原则的合理例外——关键路径上的极少量内联脚本需要同步执行,这恰恰是为了避免更糟的阻塞形态。
在 BaseLayout.astro 中,主题初始化脚本被刻意写成<script is:inline>放在<head>的第一个可执行位置:
<script is:inline> ;(function () { var root = document.documentElement // 读取 localStorage / matchMedia,同步切换 dark/light 主题类名 })() </script>文件注释解释了原因:"Theme bootstrap MUST be the first executable content in<head>。Si se difiere, se ve un flash en modo claro antes de aplicar el tema."(主题引导必须是<head>中第一个可执行内容。若被延后,会在应用主题前出现亮色模式的闪烁)。这类"必须在首帧前同步生效、且体积极小"的引导脚本,是defer/async规则的刻意例外——对它们而言,延迟执行的代价(主题闪烁、布局闪变)远大于解析阻塞的代价,因此选择内联同步。同时布局文件用visibility: hidden兜底:若脚本被延迟,未定主题的页面不会以错误配色闪现(BaseLayout.astro)。
与之形成对照的是同文件中的非阻塞资源预加载实践:字体文件使用<link rel="preload" as="font">提前并行下载(BaseLayout.astro),图书封面图片用rel="preload" as="image"配合fetchpriority="low"提前发起请求且不与 LCP 资源竞争(BaseLayout.astro)。这些做法与脚本的 defer/async 异曲同工:在解析早期用非阻塞手段"预热"关键资源,把网络往返藏进解析时间里。
更宏观地看,astro.config.mjs 中inlineStylesheets: 'always'的注释也讲述了同一套性能哲学:将 CSS 内联进 HTML 以避免"浏览器解析完 head 才发现样式表链接"这一额外的串行 round-trip,从而保证首屏立即可绘制。可见本仓库对渲染阻塞问题(脚本、样式、字体、图片)是系统性地处理,而非单点修补。
决策速查与边界提醒
将规则内化为可执行的判断,记住这张速查表:
| 场景 | 正确选择 | 理由 |
|---|---|---|
| 独立第三方脚本(分析、埋点、AB 测试) | async | 不依赖 DOM 与其他脚本,顺序无要求 |
| 依赖 DOM / 依赖其他脚本的工具库 | defer | 保证解析完成后按序执行 |
| 页面前置交互的关键脚本(Next.js) | strategy="beforeInteractive" | 需在交互前就绪,且只在根布局使用 |
| 非关键/低优先级脚本(Next.js) | strategy="afterInteractive"或lazyOnload | 完全错开首屏主线程 |
| 首帧前必须同步生效的引导逻辑 | 内联 + 刻意同步(例外) | 延迟执行的代价高于阻塞代价 |
最后提醒三条容易踩的边界:
defer不作用于内联脚本,只有src外部脚本才会被延迟执行,需要内联但可延后的逻辑应放入独立.js文件再以defer引用;async无顺序保证,若脚本间存在隐式依赖(如"先埋点后上报"),务必使用defer而非async,否则可能因顺序颠倒引入难以排查的线上问题;- 规则适用前提:本条规则针对浏览器端的第三方/工具脚本加载。对于框架内置的打包产物(如 Astro 构建出的模块脚本、React 运行时 chunk),应由框架自身的加载策略管理,不要手动在页面里再加一层
<script src>,否则反而破坏框架的代码分割与优先级调度。
结合本仓库的技能库组织方式,你可以把这条规则与其他渲染性能规则(如 rendering-resource-hints.md 中 React DOM 的preload/preconnectAPI)组合使用:前者解决"脚本何时执行",后者解决"关键资源何时开始下载",二者协同才能在首屏路径上做到既快又稳。
- 前端
- 教程
【免费下载链接】preguntas-entrevista-react
Preguntas típicas sobre React para entrevistas de trabajo ⚛️
相关推荐
Cherry Studio React 渲染性能实践:用 defer/async 消除 Script 标签的渲染阻塞
Cherry Studio React 渲染性能实践:用 defer/async 消除 Script 标签的渲染阻塞 本文讲解 Vercel React 最佳实
人工智能大模型AI 应用交互助手本地部署AI4Animation 引用完整指南:5 分钟写对角色动画项目的论文引用
AI4Animation 引用完整指南:5 分钟写对角色动画项目的论文引用 如果你在论文里用了 AI4Animation——这个用深度学习做角色动画控制的开源项
人工智能深度学习图形学游戏开发Polar Web 前端 Script 加载性能指南:用 defer/async 与 next/script 消除渲染阻塞
Polar Web 前端 Script 加载性能指南:用 defer/async 与 next/script 消除渲染阻塞 Polar(开源结算平台)的 Web
后端前端金融科技
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考