这是「前端向学习」系列的第一篇。本篇把 11 个前端基础知识点串成一条线,把常见误区和易混点直接融进对应章节讲解,而不是堆在文末。

写在前面

本篇是「前端向学习」系列的第一篇,把 11 个前端基础知识点串成一条线。讲解以直接说清前端概念为主,少代码、多直觉;常见的误区和易混点会在对应的章节里当场讲清,而不是堆在文末统一答疑。


本文脉络:先看结论

时间有限的话,先记住下面这张「骨架图」和 5 条核心认知,再带着它们去读细节。

本篇会把下面 11 个前端基础知识点串成一条线:

SEO → async/defer → DOMContentLoaded → 事件家族 → 行内/块级 → preload/prefetch → async vs as → CSS3 新特性 → 盒模型 → 选择器优先级 → 动效三剑客。

读的时候重点建立这 5 条认知:

  1. 概念之间是有联系的:把「脚本加载 / 事件 / 盒模型」串起来看,比单点记忆更牢;
  2. SPA 纯 CSR 是 SEO 最大坑,要靠 SSR/预渲染;
  3. defer 在 DOMContentLoaded 前跑完、能安全操作 DOM;async 不保证顺序;
  4. 盒模型默认 content-box 会撑破布局,全局 border-box 最省心;
  5. 动效三剑客角色不同:transform 管「变什么」,transition 管「平滑过渡」,animation 管「自动多帧」。

一、SEO:让搜索引擎找得到、读得懂、排得前

1.1 SEO 是什么

SEO = Search Engine Optimization(搜索引擎优化)。目标只有一个:让搜索引擎「找得到、读得懂、排得前」你的网站,从而带来免费的自然搜索流量。

SEO 核心流程(前端向学习一)

1.2 为什么需要 SEO

  • 自然搜索流量 = 免费 + 持续 + 高意向:用户主动搜关键词找上门,比广撒广告精准得多;
  • 不做 SEO 的网站:内容再好,别人搜不到等于不存在;
  • 广告流量一停就断,SEO 是「睡后流量」基本盘:排名做上来后长期稳定带来访问。

1.3 怎么实现:三类 + 前端主攻区

SEO 分技术、内容、站外三类。前端真正能落地的主要是「技术 SEO」:

类别 谁负责 前端能做的
技术 SEO 前端 语义化标签、SSR/预渲染、sitemap/robots、速度&移动端
内容 SEO 运营/编辑 关键词、原创度、更新频率
站外 SEO 市场/运营 外链、社媒、品牌

前端最该盯的 4 件事:

  1. 语义化 HTML:<title>、<meta name="description">、<h1>~`
    、图片 alt`。相当于给响应体写清「字段说明」,爬虫才知道每段是啥。
  2. 渲染方式(最大坑):React/Vue 的 SPA 默认 CSR(客户端渲染)——服务器只返回空壳 HTML + JS,爬虫拿到空白页,内容不被收录。要用 SSR(Next/Nuxt)或预渲染 把内容直接塞进 HTML。
  3. 可抓取:建 sitemap.xml + robots.txt,别用 JS 跳转、别用登录墙挡住爬虫。
  4. 速度 & 移动端:Core Web Vitals(LCP/CLS/INP),响应式适配。

认知拐点:同一套 React 应用,CSR 模式下爬虫拿到的 HTML 是空的(不收录),SSR 模式下拿到的 HTML 已经带完整内容(可收录)。这一条是前端学 SEO 最容易踩的坑。


SEO 三类分工(前端向学习一) CSR 与 SSR 的爬虫视角对比(前端向学习一)

二、页面加载与脚本执行

2.1 async 与 defer 的区别

  • 普通 <script>:同步阻塞——浏览器遇到它就停,等脚本下载 + 执行完才继续解析后面的 HTML。
  • async:异步下载、下载完立即执行;多个 async 脚本谁先下完谁先跑,顺序不保证。
  • defer:异步下载、但不立即执行——等 HTML 解析完、按出现顺序执行,且在 DOMContentLoaded 前。
维度 普通 script async defer
是否阻塞 HTML 解析 是 否 否
执行时机 下载完立即执行 下载完立即执行 解析完后、DOMContentLoaded 前
执行顺序 按出现顺序 不保证 保证
能否操作完整 DOM 当时不一定 不一定 能
适用场景 几乎不用 独立无依赖脚本 有依赖、要操作 DOM 的脚本
async 与 defer 的脚本加载执行时序(前端向学习一)

两个必记的坑:

  1. async 和 defer 只对「外部脚本」(src="...")有效,内联脚本加这俩没用;
  2. 现代最佳实践:业务脚本默认 defer;只有完全独立、不碰 DOM、不依赖别的脚本(统计埋点、广告 SDK)才用 async。

💡 扩展(面试常考):<script type="module">(ES Module)默认就是 defer 行为——异步下载、按出现顺序执行、在 DOMContentLoaded 前跑完,所以模块化脚本天然不会阻塞解析,且多个 module 之间顺序有保障。此外,async 脚本之间如果有依赖关系会出错(顺序不保证),所以异步加载又需要顺序时,应该用 defer 或 type="module",而不是 async。

2.2 DOMContentLoaded 到底是什么(不是「前端渲染的内容」)

一个常见的误解是:在 DOMContentLoaded 事件前,是不是就代表「前端渲染好的内容」?——不是。

DOMContentLoaded 不是「画面渲染好了」,而是「DOM 树在内存里建好了」这个时刻发出的通知。 你看到的画面(paint)和 DOM 树(结构)是两件独立的事:

  • DOM 树 = 浏览器把 HTML 标签解析成一棵内存节点树(<div>、<p> 这些节点对象都在了);
  • 画面渲染(paint) = 浏览器把「DOM 树 + CSS」算成像素画到屏幕上,从解析一开始就在逐步进行,图片/字体加载完还会重画。

所以 DOMContentLoaded 只保证「结构好了」,不保证「画好了」「图片加载了」。

完整时间线:

  1. 解析 HTML,构建 DOM 树;
  2. 执行 defer 脚本(按出现顺序,此时 DOM 已完整,可安全操作任意节点);
  3. 触发 DOMContentLoaded(通知:结构就绪);
  4. 图片 / CSS 等大资源继续加载;
  5. 触发 load(通知:全部资源就绪)

。

而 paint(画面) 从步骤 1 起就在底层逐步进行,贯穿全程。

defer 脚本并不是「监听」DOMContentLoaded,而是浏览器保证:在吹这声集结号之前,先把所有 defer 脚本按序跑完。所以「在 DOMContentLoaded 事件前」= defer 脚本是吹号前的必要准备,执行时 DOM 已完整但号还没响。

对比 async:浏览器不等它,DOMContentLoaded 也不等它 → 它可能在 DOMContentLoaded 前也可能在后。这也是 async 不能保证顺序的原因。

页面加载关键时间点(前端向学习一)

2.3 还有别的事件吗:事件家族

DOMContentLoaded/load 只是「文档/资源生命周期」这一个家族。顺着加载线还有:

事件 触发时机 典型用途
readystatechange document.readyState 每次变化 DOMContentLoaded/load 本质是它走到 interactive/complete 的语义化别名
beforeunload 用户要离开(关标签/刷新) 弹「确认离开?」、做关键保存
pagehide / unload 页面真正卸载 真正做清理(埋点上报、断开连接);unload 不可靠,现代用 pagehide
visibilitychange 标签页切后台/前台 切后台暂停定时器/轮询,切回来恢复
online / offline 网络通/断 断网提示、失败重试

整个「事件宇宙」还分:用户交互(click/input/submit)、网络(fetch/XMLHttpRequest 的 load/error、<img>.onload)、观察者 API(IntersectionObserver/ResizeObserver/MutationObserver)、动画渲染(requestAnimationFrame/requestIdleCallback)、错误(error/unhandledrejection)。

用哪个事件? 操作 DOM

结构 → DOMContentLoaded(或 defer);等图片/全部资源 → load;切后台暂停/埋点 → visibilitychange;离开清理 → beforeunload/pagehide;元素进视口再加载 → IntersectionObserver。

文档生命周期事件全景(前端向学习一)

2.4 preload 与 prefetch:资源提示

preload 和 prefetch 都是写在 <link rel="..."> 上的「资源提示(resource hint)」,让浏览器提前下载资源——区别在「为谁、何时、什么优先级」。

  • preload:为当前页面的关键资源(首屏 CSS/JS/字体/首图)立即高优先级下载,用到即命中;
  • prefetch:为未来页面(如用户下一步可能去的详情页)在浏览器空闲时低优先级下载,用不用得到不一定。
维度 preload prefetch
为谁 当前页面关键资源 未来页面 / 后续导航
时机 立即,与页面资源并行 浏览器空闲时
优先级 高 低
必填属性 as(如 as="script") 不需要 as
典型用途 首屏关键 CSS/JS、首图、字体 列表页预取详情页 JS
preload 与 prefetch 时序对比(前端向学习一)

2.5 as 是什么?不是 async!

上一节 preload 的写法里反复出现 as="script"、as="style" 这种字段。它和前面讲过的 async 长得很像、又常常同时出现,所以特别容易混为一谈。

先一句话区分:async 管「脚本的执行时机」,as 管「预加载时声明资源类型」——两者属于完全不同的 API,毫无关系。

as 是 <link rel="preload">(以及 prefetch / modulepreload 这类 resource hint)上的必填属性,告诉浏览器预加载的是什么类型资源。浏览器拿到 as 后会做三件事:① 定正确的下载优先级(脚本、样式高优,字体中等);② 套正确的安全策略(如 CORS);③ 做匹配校验(写错类型会导致「预加载一份、实际请求又下一份」的重复下载)。

async 与 as 不是一回事(前端向学习一)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16




<link rel="preload" href="/js/app.js" as="script">
<script src="/js/app.js"></script>


<link rel="preload" href="/css/main.css" as="style">
<link rel="stylesheet" href="/css/main.css">


<link rel="preload" href="/fonts/icon.woff2" as="font" type="font/woff2" crossorigin>


<link rel="preload" href="/img/hero.jpg" as="image">
as 值 对应资源 备注
script JS —
style CSS —
font 字体 必须加 crossorigin
image 图片 —
fetch fetch/XHR 数据 接口数据也可预取
video / audio 音视频 —
worker Web Worker —

两个坑:① 漏写 as → preload 直接失效并报警告;② as="font" 必须加 crossorigin,否则「预加载一份、实际请求又下一份」。

💡 扩展(资源提示全家桶):preload/prefetch 只是 resource hint 的两种。还有几个实战常用:dns-prefetch(提前做 DNS 解析,省解析耗时)、preconnect(提前建好 TCP + TLS 连接,比 dns-prefetch 更彻底,省握手延迟)、modulepreload(预加载 ES module)。跨域请求(如你的 Agnes API、独立图床)强烈建议配 preconnect,能把「建连 + TLS 握手」几十到上百毫秒的耗时从关键路径上挪走。


三、HTML 元素模型与盒模型

3.1 行内元素与块级元素有哪些

  • 块级(block)元素 ≈ 文档里「独占一行的区块」,前后自动换行,默认撑满整行。像日志里每条记录单独成行。
  • 行内(inline)元素 ≈ 句子里「夹着的着重标记/链接」,跟文字混排在同一行,宽度只够包住内容;设 width/height 无效。
  • 行内块(inline-block) ≈ 「既能并排、又能指定尺寸的小组件」,能定宽高(如 img/input/button)。

常见清单:

  • 块级(默认 display:block):div p h1~`h6 ul ol li table form blockquote pre hr header footer section article nav main aside address fieldset`
  • 行内(默认 display:inline):span a strong em b i u small code label br sub sup abbr cite q time mark bdo
  • 行内块(默认 display:inline-block):img input button select textarea
维度 块级 block 行内 inline 行内块 inline-block
是否换行 独占一行 不换行、并排 不换行、并排
宽高 可设,默认撑满父宽 设了无效 可设
上下 margin/padding 生效 上下不占布局空间 生效

注意:这三类说的是默认 display 值,现代 CSS 可用 display 任意改写。HTML5 语义标签(header/section/article…)默认都是块级,布局和 div 等价,区别只在语义/SEO。

行内、块级、行内块布局对比(前端向学习一)

3.2 CSS 盒模型:四层拆解

盒模型是 CSS 的地基——你写的每个 div、span,浏览器都当成一个矩形盒子来摆。由内到外四层:content → padding → border → margin。

把盒子想成一个快递包裹就很好懂

:content 是商品,padding 是商品到盒内壁的泡沫,border 是包装盒本身(有厚度、占空间),margin 是多个包裹之间的空隙——不计入盒子自身尺寸,也不显示背景色。

CSS 盒模型四层结构(前端向学习一)

3.3 两种盒模型(核心考点)

box-sizing 决定你写的 width/height 算到哪一层:

模式 width 包含 设了 padding/border 的后果
content-box(默认) 仅 content 实际占位被撑大 = width + padding + border
border-box(推荐) content+padding+border 不撑破,content 自动缩小去容纳

设定完全一样:width:200px + padding:20px + border:5px,看两种模式下「你写的 200 到底量的是哪一段」:

一句话记:content-box ≈ 内径定价、外径加钱(说内径 200,外径另加包装费);border-box ≈ 外径包干(说外径 200,包装费全包,内径让位)。

最典型的坑:想让元素 width:100% 填满父容器,加了点 padding 就横向溢出(content-box 下 100% + padding 远超父宽)。改成 border-box 后永远不溢出。

💡 扩展(margin 折叠 / collapsing):相邻块级元素的上下 margin 会「取较大值而非相加」——比如上面元素 margin-bottom:30px、下面元素 margin-top:20px,它们之间的间距是 30px 而不是 50px。左右 margin 不折叠。另外,父子元素间若没有 border/padding/内容隔开,也会发生 margin 折叠「穿透」。这是布局时「间距对不上预期」的常见元凶,记住它比死记公式更实用。

content-box 与 border-box 对比(前端向学习一)

3.4 盒模型在哪里配置

box-sizing 是个 CSS 属性,写在对应规则里,值取 content-box(默认)或 border-box。配置位置分两层:

1
2
3
4
5
/* 1. 全局配置(最常用、推荐):放在 CSS 文件最顶部 */
*, *::before, *::after { box-sizing: border-box; }

/* 2. 单个元素 / 某类元素 */
.card { box-sizing: border-box; }

关键坑:box-sizing 不继承(和 color 不同)。父元素设了,子元素不会自动继承。所以要么用全局 *,要么每个需要的元素单独写。这也是为什么「全局重置」是主流写法。


四、CSS3 新特性概览

先纠一个误解:CSS3 不是「一门新语言」,而是 CSS 规范的第 3 个大版本,关键变化是——从「一整本大文档」拆成了一堆独立模块(Selectors、Flexbox、Grid、Transitions、Animations、Media Queries…),各自演进、各自定级。所以没有统一的 CSS3 版本号

,有的特性(Grid)很晚普及,有的(圆角)早就支持。

CSS3 新特性分类地图(前端向学习一)

六大模块地图:

模块 关键属性/语法 一句话用途
布局 flex / grid / box-sizing 一维/二维/盒模型
动效 transition / @keyframes / transform 平滑过渡与动画
选择器 :nth-child / :not / [attr] 结构化条件选取
视觉 border-radius / shadow / gradient / rgba 圆角阴影渐变透明
响应式 @media / vw·vh / srcset 多端适配
变量 --var() / calc() / @font-face 全局常量与计算

五、CSS 选择器优先级

当多个 CSS 规则同时命中同一个元素时,浏览器按「优先级(specificity)」挑赢家——选择器写得越具体(ID > 类 > 标签),优先级越大。

从高到低: !important

行内 style > #id > .class / [attr] / :hover > div / ::before > * 通配 / 继承

CSS 选择器优先级梯度(前端向学习一)

权重四元组 (a,b,c,d):

  • a:行内样式(1 或 0)
  • b:ID 选择器个数
  • c:类 / 属性 / 伪类个数
  • d:元素 / 伪元素个数

比较规则:从左到右逐位比,谁大谁赢,不进位。所以 1 个 ID (0,1,0,0) 永远大于 100 个类 (0,0,100,0),不是十进制加法。

1
2
#nav .item a:hover   /* (0,1,2,1) —— b 位有 1,直接赢 */
div.box p /* (0,0,1,2) */

两个兜底: ① 优先级相同 → 后写的覆盖先写的;② !important 强制最高,但强烈不建议滥用(打乱层叠逻辑、制造调试噩梦,只在覆盖第三方库时迫不得已用)。

实用建议: 别用 ID 写样式(优先级太高、难覆盖、破坏复用);别随便 !important;想稳赢加「类」而不是加「ID」。

💡 扩展(:is() / :where() 的优先级陷阱):现代 CSS 的 :is() 会「取括号内最高优先级的选择器」参与计算,而 :where() 优先级恒为 0(无论里面写多复杂)。所以想写一组选择器又不想拉高优先级、方便后续覆盖时,用 :where();想分组但保留原优先级时用 :is()。这是新项目里很实用、也容易踩坑的细节。


六、动效三剑客:transform / transition / animation

这三个是 CSS 动效「三剑客」,角色完全不同——最容易混的是以为它们都是「动画」。

  • transform = 形变属性(平移/旋转/缩放/倾斜),定义「元素变成什么样」。本身不产生动画,写了立即生效、瞬间到位。
  • transition = 过渡机制,定义「属性变化时怎么平滑过去」。必须有触发条件(:hover、class 切换)才会跑。
  • animation = 动画序列,用 @keyframes 定义多关键帧,自动播放、可循环,无需触发。
维度 transform transition animation
本质 形变属性 过渡机制 动画序列
是否需要触发 否(直接应用) 是(:hover/class) 否(自动)
时间维度 无(瞬间) 单次过渡 可循环
关键帧 无 仅起止两态 多帧 @keyframes
典型用途 位移/旋转/缩放 按钮 hover 平滑 loading/轮播/入场
transform transition animation 时间维度对比(前端向学习一)

组合: 单独 transform → 瞬间跳变;transform + transition → 悬停动效;transform + animation → 循环/入场/loading。

💡 扩展(重排 reflow 与重绘 repaint):这是动效性能的底层原理,也是面试高频题。

  • 重排(reflow):元素的几何属性变了(宽高、位置、布局),浏览器要重新计算整个渲染树——开销大。改 left/top/width/margin 都会触发。
  • 重绘(repaint):元素外观变了(颜色、阴影)但几何不变,只重新绘制像素——开销较小。
  • 合成(composite):transform 和 opacity 的变动走 GPU 合成层,不触发重排也不触发重绘,只把图层位移/旋转,性能最好。

所以动效优先用 transform/opacity,避开会触发重排的 left/top/width——这就是「为什么 transform 性能好」的答案。再配合 will-change: transform 可提前提示浏览器建独立合成层(但别滥用,会占显存)。

下面是一段可直接在浏览器看效果的最小 demo(对应本文第六章):

① transition + transform(悬停平滑)
把鼠标移上去
② 纯 transform(瞬间跳变)
③ animation(自动循环)
加载即循环

完整可交互版本(含说明):CSS 动效三剑客 · 可交互对照 Demo——浏览器打开即可实时体验 transform / transition / animation 三种效果的完整对照。


七、面试高频问答速答

下面把本章最常被问到的题,整理成「面试现场可以直接说的版本」。回答遵循「先给结论、再补细节」的节奏。

Q1:说一下 async 和 defer 的区别?

两者都是并行下载、不阻塞 HTML 解析。async 下载完就立即执行,多个 async 脚本顺序不保证,适合独立无依赖的脚本(统计/广告);defer 下载完但推迟到 HTML 解析结束、按出现顺序执行,且在 DOMContentLoaded 前,适合有依赖、要操作 DOM 的脚本。另外 type="module" 默认就是 defer 行为。

Q2:DOMContentLoaded 和 load 的区别?

DOMContentLoaded 是「DOM 树构建完成」的信号,此时结构就绪、可安全操作 DOM,但不代表图片/样式加载完;load 是「所有资源(图片、样式、iframe 等)都加载完成」的信号。defer 脚本在 DOMContentLoaded 前跑完,async 不一定。要等图片尺寸就用 load,但要警惕 load 可能因某张大图迟迟不触发。

Q3:块级元素和行内元素的区别?

块级(div/p/h1…)独占一行、默认撑满父宽、可设宽高和上下 margin;行内(span/a/strong…)并排不换行、宽度由内容撑开、设 width/height 无效、上下 margin 不占布局空间。行内块(img/input/button)则并排又能定宽高。这些只是默认 display 值,都能用 CSS 改写。

Q4:什么是 CSS 盒模型?content-box 和 border-box 区别?

每个元素渲染成一个矩形盒子,由内到外四层:content → padding → border → margin,margin 是外部空隙、不计入自身尺寸。box-sizing 决定 width 算到哪层:content-box(默认)只算 content,加了 padding/border 会撑大实际占位;border-box 把 padding/border 算进 width,内容自动内缩,永不溢出。所以项目里几乎都写全局 * { box-sizing: border-box }。

Q5:CSS 选择器优先级怎么算?

用四元组 (a,b,c,d) = (行内, ID 个数, 类/属性/伪类个数, 元素/伪元素个数),从左到右逐位比、不进位——所以 1 个 ID 永远大于 100 个类。顺序是:!important > 行内 > ID > 类 ≈ 属性 ≈ 伪类 > 元素 ≈ 伪元素 > 通配/继承。同级后写覆盖先写。!important 能压一切但别滥用;另外 :where() 优先级恒为 0、:is() 取括号内最高。

Q6:transform、transition、animation 的区别?

transform 是形变属性(位移/旋转/缩放),本身无动画、瞬间生效;transition 是过渡机制,定义「属性变化怎么平滑过去」,需 :hover/class 切换触发、单次;animation 是用 @keyframes 定义的多帧序列,自动播放、可循环、无需触发。常见组合:transform+transition 做悬停,transform+animation 做循环/loading。

Q7:为什么说 transform 性能好?什么是重排重绘?

改动 left/top/width 会触发重排(重新计算布局,开销最大)和重绘;改颜色触发重绘;而 transform/opacity 走 GPU 合成层,不触发重排也不重绘,只位移/旋转图层,所以性能最好。动效优先用 transform/opacity,避免触发重排的属性。

Q8:preload 和 prefetch 的区别?

preload 为当前页面的关键资源(首屏 CSS/JS/字体/首图)立即高优先级下载,必须写 as 声明类型,用到即命中;prefetch 为未来页面(如下一页 JS)在浏览器空闲时低优先级下载,用不用得到不一定。字体 preload 必须加 crossorigin,否则会重复下载。


本篇完。开头那张「11 点脉络 + 5 条核心认知」就是这份笔记的骨架,需要快速回顾时回开头看一眼即可。