这是「前端向学习」系列的第一篇。本篇把 11 个前端基础知识点串成一条线,把常见误区和易混点直接融进对应章节讲解,而不是堆在文末。
写在前面
本篇是「前端向学习」系列的第一篇,把 11 个前端基础知识点串成一条线。讲解以直接说清前端概念为主,少代码、多直觉;常见的误区和易混点会在对应的章节里当场讲清,而不是堆在文末统一答疑。
本文脉络:先看结论
时间有限的话,先记住下面这张「骨架图」和 5 条核心认知,再带着它们去读细节。
本篇会把下面 11 个前端基础知识点串成一条线:
SEO → async/defer → DOMContentLoaded → 事件家族 → 行内/块级 → preload/prefetch → async vs as → CSS3 新特性 → 盒模型 → 选择器优先级 → 动效三剑客。
读的时候重点建立这 5 条认知:
- 概念之间是有联系的:把「脚本加载 / 事件 / 盒模型」串起来看,比单点记忆更牢;
- SPA 纯 CSR 是 SEO 最大坑,要靠 SSR/预渲染;
defer在 DOMContentLoaded 前跑完、能安全操作 DOM;async不保证顺序;- 盒模型默认
content-box会撑破布局,全局border-box最省心; - 动效三剑客角色不同:transform 管「变什么」,transition 管「平滑过渡」,animation 管「自动多帧」。
一、SEO:让搜索引擎找得到、读得懂、排得前
1.1 SEO 是什么
SEO = Search Engine Optimization(搜索引擎优化)。目标只有一个:让搜索引擎「找得到、读得懂、排得前」你的网站,从而带来免费的自然搜索流量。
1.2 为什么需要 SEO
- 自然搜索流量 = 免费 + 持续 + 高意向:用户主动搜关键词找上门,比广撒广告精准得多;
- 不做 SEO 的网站:内容再好,别人搜不到等于不存在;
- 广告流量一停就断,SEO 是「睡后流量」基本盘:排名做上来后长期稳定带来访问。
1.3 怎么实现:三类 + 前端主攻区
SEO 分技术、内容、站外三类。前端真正能落地的主要是「技术 SEO」:
| 类别 | 谁负责 | 前端能做的 |
|---|---|---|
| 技术 SEO | 前端 | 语义化标签、SSR/预渲染、sitemap/robots、速度&移动端 |
| 内容 SEO | 运营/编辑 | 关键词、原创度、更新频率 |
| 站外 SEO | 市场/运营 | 外链、社媒、品牌 |
前端最该盯的 4 件事:
- 语义化 HTML:
<title>、<meta name="description">、<h1>~`、图片alt`。相当于给响应体写清「字段说明」,爬虫才知道每段是啥。 - 渲染方式(最大坑):React/Vue 的 SPA 默认 CSR(客户端渲染)——服务器只返回空壳 HTML + JS,爬虫拿到空白页,内容不被收录。要用 SSR(Next/Nuxt)或预渲染 把内容直接塞进 HTML。
- 可抓取:建
sitemap.xml+robots.txt,别用 JS 跳转、别用登录墙挡住爬虫。 - 速度 & 移动端:Core Web Vitals(LCP/CLS/INP),响应式适配。
认知拐点:同一套 React 应用,CSR 模式下爬虫拿到的 HTML 是空的(不收录),SSR 模式下拿到的 HTML 已经带完整内容(可收录)。这一条是前端学 SEO 最容易踩的坑。
二、页面加载与脚本执行
2.1 async 与 defer 的区别
- 普通
<script>:同步阻塞——浏览器遇到它就停,等脚本下载 + 执行完才继续解析后面的 HTML。 async:异步下载、下载完立即执行;多个 async 脚本谁先下完谁先跑,顺序不保证。defer:异步下载、但不立即执行——等 HTML 解析完、按出现顺序执行,且在DOMContentLoaded前。
| 维度 | 普通 script | async | defer |
|---|---|---|---|
| 是否阻塞 HTML 解析 | 是 | 否 | 否 |
| 执行时机 | 下载完立即执行 | 下载完立即执行 | 解析完后、DOMContentLoaded 前 |
| 执行顺序 | 按出现顺序 | 不保证 | 保证 |
| 能否操作完整 DOM | 当时不一定 | 不一定 | 能 |
| 适用场景 | 几乎不用 | 独立无依赖脚本 | 有依赖、要操作 DOM 的脚本 |
两个必记的坑:
async和defer只对「外部脚本」(src="...")有效,内联脚本加这俩没用;- 现代最佳实践:业务脚本默认
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 只保证「结构好了」,不保证「画好了」「图片加载了」。
完整时间线:
- 解析 HTML,构建 DOM 树;
- 执行
defer脚本(按出现顺序,此时 DOM 已完整,可安全操作任意节点); - 触发
DOMContentLoaded(通知:结构就绪); - 图片 / CSS 等大资源继续加载;
- 触发
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 |
2.5 as 是什么?不是 async!
上一节 preload 的写法里反复出现 as="script"、as="style" 这种字段。它和前面讲过的 async 长得很像、又常常同时出现,所以特别容易混为一谈。
先一句话区分:async 管「脚本的执行时机」,as 管「预加载时声明资源类型」——两者属于完全不同的 API,毫无关系。
as 是 <link rel="preload">(以及 prefetch / modulepreload 这类 resource hint)上的必填属性,告诉浏览器预加载的是什么类型资源。浏览器拿到 as 后会做三件事:① 定正确的下载优先级(脚本、样式高优,字体中等);② 套正确的安全策略(如 CORS);③ 做匹配校验(写错类型会导致「预加载一份、实际请求又下一份」的重复下载)。
1 |
|
| 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):divph1~`h6ulollitableformblockquoteprehrheaderfootersectionarticlenavmainasideaddressfieldset` - 行内(默认
display:inline):spanastrongembiusmallcodelabelbrsubsupabbrciteqtimemarkbdo - 行内块(默认
display:inline-block):imginputbuttonselecttextarea
| 维度 | 块级 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 是多个包裹之间的空隙——不计入盒子自身尺寸,也不显示背景色。
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 折叠「穿透」。这是布局时「间距对不上预期」的常见元凶,记住它比死记公式更实用。
3.4 盒模型在哪里配置
box-sizing 是个 CSS 属性,写在对应规则里,值取 content-box(默认)或 border-box。配置位置分两层:
1 | /* 1. 全局配置(最常用、推荐):放在 CSS 文件最顶部 */ |
关键坑:box-sizing 不继承(和 color 不同)。父元素设了,子元素不会自动继承。所以要么用全局 *,要么每个需要的元素单独写。这也是为什么「全局重置」是主流写法。
四、CSS3 新特性概览
先纠一个误解:CSS3 不是「一门新语言」,而是 CSS 规范的第 3 个大版本,关键变化是——从「一整本大文档」拆成了一堆独立模块(Selectors、Flexbox、Grid、Transitions、Animations、Media Queries…),各自演进、各自定级。所以没有统一的 CSS3 版本号
,有的特性(Grid)很晚普及,有的(圆角)早就支持。
六大模块地图:
| 模块 | 关键属性/语法 | 一句话用途 |
|---|---|---|
| 布局 | 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>*通配 / 继承
权重四元组 (a,b,c,d):
- a:行内样式(1 或 0)
- b:ID 选择器个数
- c:类 / 属性 / 伪类个数
- d:元素 / 伪元素个数
比较规则:从左到右逐位比,谁大谁赢,不进位。所以 1 个 ID (0,1,0,0) 永远大于 100 个类 (0,0,100,0),不是十进制加法。
1 | #nav .item a:hover /* (0,1,2,1) —— b 位有 1,直接赢 */ |
两个兜底: ① 优先级相同 → 后写的覆盖先写的;② !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 → 瞬间跳变;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(对应本文第六章):
完整可交互版本(含说明):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 条核心认知」就是这份笔记的骨架,需要快速回顾时回开头看一眼即可。




