一、什么是语义化HTML?列举5个以上语义化标签及使用场景
📝 标准答案
语义化HTML指的是用具有明确含义的HTML标签来描述内容结构,而非使用无意义的<div>和<span>堆砌。它让人(开发者)和机器(浏览器、搜索引擎、屏幕阅读器)都能清晰理解页面结构。
🔥 高频语义化标签清单
| 标签 | 语义 | 使用场景 |
|---|---|---|
<header> | 页眉/头部 | 网站LOGO、导航栏、搜索框 |
<nav> | 导航链接 | 主导航菜单、面包屑 |
<main> | 主内容区(页面仅能有一个) | 页面核心正文区域 |
<article> | 独立、完整的内容块 | 博客文章、新闻稿件、用户评论 |
<section> | 通用区块(带标题) | 文章中的章节、Tab面板 |
<aside> | 侧边栏/附属信息 | 广告位、相关推荐、作者简介 |
<footer> | 页脚 | 版权信息、友情链接、备案号 |
<figure>+<figcaption> | 图文组合 | 配图+图注 |
<time> | 时间日期 | 文章发布时间 |
<mark> | 高亮文本 | 搜索结果中的关键词高亮 |
💻 代码演示
❌反面教材(div地狱):
<div class="header"> <div class="nav">导航</div> </div> <div class="main"> <div class="content"> <div class="title">文章标题</div> <div class="text">正文内容...</div> </div> <div class="sidebar">侧边栏</div> </div> <div class="footer">版权信息</div>
✅正确姿势(语义化):
<header> <nav>导航</nav> </header> <main> <article> <h1>文章标题</h1> <p>正文内容...</p> <time datetime="2026-07-14">2026年7月14日</time> </article> <aside>侧边栏</aside> </main> <footer>版权信息</footer>
🎯 面试官追问
Q:语义化对SEO具体有什么提升?
A:搜索引擎爬虫抓取HTML时,语义化标签相当于给内容加上了“身份标签”。例如,
<h1>标签内的内容权重高于普通文本,<article>告诉爬虫“这是独立内容”。配合<meta>描述标签,能显著提升关键词排名。
Q:<section>和<article>怎么区分?
A:
<article>是独立可分发的内容(即使脱离上下文也有意义);<section>是文档中的逻辑分区,必须带标题(<h1>-<h6>)。如果搞不清,用<div>都行,但别乱用<section>。
二、<!DOCTYPE html>的作用是什么?标准模式 vs 怪异模式
📝 标准答案
<!DOCTYPE html>是文档类型声明,位于HTML文档最顶部,告诉浏览器以标准模式(Standards Mode)解析HTML/CSS,而非历史遗留的怪异模式(Quirks Mode)。
🧠 深度解析
IE6时代遗留了两个渲染模式:
标准模式(Standards Mode):按照W3C规范严格渲染,盒模型正确(
width= 内容宽度,padding/border在外部增加)。怪异模式(Quirks Mode):兼容IE5.5以下老页面,盒模型错误(
width包含了padding和border),且不支持CSS2.1的很多特性。
没有写<!DOCTYPE>或写错(如过时的DTD声明),IE会触发怪异模式,导致布局全面崩坏。
💻 盒模型对比代码
/* 标准模式下 */ .box { width: 200px; padding: 20px; border: 2px solid red; /* 实际占用宽度 = 200 + 40 + 4 = 244px ✅ */ } /* 怪异模式下(不加 DOCTYPE) */ .box { width: 200px; padding: 20px; border: 2px solid red; /* 内容宽度被压缩 = 200 - 40 - 4 = 156px ❌ 完全错乱 */ }🎯 面试官追问
Q:现在HTML5还需要写<!DOCTYPE>吗?
A:必须写!虽然现代浏览器默认标准模式,但少了这行声明,部分老旧浏览器(如IE10以下)仍会进入怪异模式。此外,它也是HTML5文档的唯一标识,放在首行是W3C强制规范。
三、<script>标签的defer和async到底怎么选?
📝 标准答案
defer和async都是异步加载外部JS文件的属性,区别在于执行时机:
async:下载完成后立即执行,执行时阻塞HTML解析,不保证执行顺序(谁先下载完谁执行)。defer:下载过程中不阻塞HTML解析,在HTML解析完成后(DOMContentLoaded事件前)按顺序执行。
💻 代码演示
<!DOCTYPE html> <html> <head> <!-- ✅ defer:等DOM建好再执行,且按顺序 --> <script defer src="jquery.js"></script> <script defer src="app.js"></script> <!-- 保证app在jquery之后 --> <!-- ❌ async:不保证顺序,适合独立脚本 --> <script async src="google-analytics.js"></script> <script async src="baidu-stat.js"></script> <!-- 谁先到谁执行 --> </head> <body> <div id="app">页面内容</div> </body> </html>
🎯 面试官追问
Q:DOMContentLoaded和load事件,哪个触发更早?
A:
DOMContentLoaded更早!它触发时只要求DOM树构建完成(CSS/图片/async脚本可能未加载完);load要等所有资源(图片、样式、iframe)全部加载完成。defer脚本在DOMContentLoaded之前执行。
Q:<script>放在<head>和<body>底部有什么区别?
A:放
<head>会阻塞首屏渲染(除非加defer/async);放<body>底部虽然不阻塞渲染,但下载依然阻塞DOM解析。最优实践:关键JS用defer放<head>,第三方统计用async。
四、前端本地存储「四大天王」区别与应用
📝 标准答案
| 特性 | Cookie | LocalStorage | SessionStorage | IndexedDB |
|---|---|---|---|---|
| 容量 | ~4KB | ~5-10MB | ~5-10MB | 无限(GB级别) |
| 有效期 | 可设置Expires/Max-Age | 永久(除非手动清除) | 当前会话(关闭Tab即失) | 永久(可手动清除) |
| 是否随请求发送 | ✅ 自动携带(同源) | ❌ 不发送 | ❌ 不发送 | ❌ 不发送 |
| 数据类型 | String | String | String | 结构化数据(对象/文件/Blob) |
| 操作方式 | document.cookie | setItem/getItem | setItem/getItem | 异步API(事务/游标) |
| 跨Tab通信 | ❌(需轮询) | ✅ 触发storage事件 | ❌(独立隔离) | ✅ 可共享 |
💻 代码演示:封装带过期时间的LocalStorage
// 面试常考:手写一个带过期时间的localStorage工具 const storageUtil = { set(key, value, expireTime = 7 * 24 * 60 * 60 * 1000) { const data = { value: value, expireAt: Date.now() + expireTime }; localStorage.setItem(key, JSON.stringify(data)); }, get(key) { const raw = localStorage.getItem(key); if (!raw) return null; const data = JSON.parse(raw); if (Date.now() > data.expireAt) { localStorage.removeItem(key); return null; // 已过期 } return data.value; }, remove(key) { localStorage.removeItem(key); } }; // 使用:存储用户token,30分钟后过期 storageUtil.set('userToken', 'abc123xyz', 30 * 60 * 1000); console.log(storageUtil.get('userToken')); // 30分钟内有效,之后返回null🎯 面试官追问
Q:Cookie的HttpOnly和Secure属性有什么用?
A:
HttpOnly防止JS通过document.cookie读取,防御XSS窃取;Secure确保Cookie只在HTTPS连接中传输,防御中间人攻击。登录态Session ID必须同时开启这两个属性。
Q:LocalStorage在无痕模式下会怎样?
A:Safari和Firefox的无痕模式会拒绝写入(抛出
QuotaExceededError),Chrome允许写入但在关闭所有标签页后自动清空。生产环境必须用try/catch包裹。
五、从输入URL到页面呈现,HTML解析经历了什么?(CRP)
📝 标准答案
这是前端面试第一高频题,完整过程分为6个阶段:
DNS解析:将域名转为IP地址(走本地缓存/递归查询)。
TCP连接:三次握手建立连接(HTTPS多一次TLS握手)。
发送HTTP请求:浏览器构建请求行/头/体,发送给服务器。
服务器处理并返回响应:返回HTML文档(状态码200/304/301等)。
浏览器解析与渲染(关键渲染路径CRP):
解析HTML → 构建DOM树(Token化→词法分析→构建节点)
解析CSS → 构建CSSOM树(CSS规则树)
DOM + CSSOM → 合并为渲染树(Render Tree)(剔除
display:none)布局(Layout):计算每个节点的几何位置(宽高、坐标)
绘制(Paint):将像素绘制到屏幕上(栅格化、合成层)
加载子资源:遇到图片/JS/CSS继续发起请求,异步加载。
📊 关键阻塞规则(面试必背)
CSS阻塞渲染:CSSOM未构建完成,页面不会渲染(避免FOUC闪屏)。
JS阻塞解析:遇到
<script>(无defer/async),浏览器暂停HTML解析,先去下载执行JS(因为JS可能修改DOM)。CSS阻塞JS执行:如果JS在CSS之后,JS会等待CSSOM构建完再执行(因为JS可能获取样式)。
💻 代码演示:观察渲染顺序
<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="slow.css"> <!-- 假设加载耗时3秒 --> <script> console.log('内联JS执行:此时CSS尚未加载完'); // 但是!CSS未加载完之前,此JS不会执行(因为CSS阻塞JS) // 实际表现:console.log会在3秒后CSS加载完才打印 </script> </head> <body> <h1>页面内容</h1> <!-- 会等CSS加载完才渲染 --> <script defer src="deferred.js"></script> <!-- defer不阻塞 --> </body> </html>🎯 面试官追问
Q:什么是回流(Reflow)和重绘(Repaint)?如何优化?
A:
回流:布局属性改变(宽高、位置、字体),浏览器重新计算几何位置,代价极高。
重绘:外观属性改变(颜色、背景、阴影),只重新绘制像素,代价较低。
优化:批量修改DOM(用
DocumentFragment)、display:none离屏操作、transform/opacity走合成器(不触发回流)。
六、src和href的区别
📝 标准答案
核心区别:src是替换型资源,href是关联型资源。
| 属性 | 作用 | 浏览器行为 | 阻塞解析? | 典型标签 |
|---|---|---|---|---|
| src | 将外部资源嵌入/替换到当前位置 | 下载后替换当前元素内容 | 阻塞(暂停解析) | <img>、<script>、<iframe> |
| href | 建立当前文档与外部资源的关联关系 | 并行下载,不影响当前文档解析 | 不阻塞 | <link>、<a> |
💻 代码演示
<!-- src:下载完替换img内容,不影响后续解析(img不阻塞) --> <img src="photo.jpg" alt="图片"> <!-- src:下载并执行JS,阻塞解析(无defer/async时) --> <script src="heavy.js"></script> <!-- href:建立样式关联,并行下载,不阻塞HTML解析(但CSS阻塞渲染) --> <link href="style.css" rel="stylesheet"> <!-- href:锚点链接,仅做导航关联 --> <a href="https://example.com">跳转</a>
🎯 面试官追问
Q:<link>的preload、prefetch和preconnect分别干什么?
A:
preload:强制提前加载当前页面关键资源(字体、关键CSS),优先级最高。
prefetch:空闲时预加载下一个页面可能用到的资源,优先级低。
preconnect:提前建立TCP/TLS握手,减少跨域请求延迟。
七、HTML层面的SEO优化,你做了几个?
📝 标准答案
HTML层面的SEO是搜索引擎友好的地基,核心优化点如下:
💻 完整优化代码示例
<!DOCTYPE html> <html lang="zh-CN"> <!-- ✅ 1. 声明语言 --> <head> <meta charset="UTF-8"> <!-- ✅ 2. 标题(核心关键词靠前,≤30字) --> <title>前端面试HTML核心考点 - 2026最新版</title> <!-- ✅ 3. 描述(吸引力强的摘要,含关键词,≤120字) --> <meta name="description" content="涵盖语义化、CRP、defer/async、本地存储等10道HTML必考面试题,附代码演示和深度追问。"> <!-- ✅ 4. 关键词(已弱化,但仍建议填写) --> <meta name="keywords" content="HTML面试题,前端面试,语义化,CRP,defer,async"> <!-- ✅ 5. Open Graph(社交分享优化) --> <meta property="og:title" content="前端面试HTML核心考点"> <meta property="og:description" content="10道高频原题+源码解析"> <meta property="og:image" content="https://example.com/cover.jpg"> <!-- ✅ 6. 移动端视口 --> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- ✅ 7. 预连接CDN加速 --> <link rel="preconnect" href="https://cdn.example.com"> <!-- ✅ 8. 结构化数据(JSON-LD)让搜索引擎理解内容 --> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Article", "headline": "前端面试HTML核心考点", "datePublished": "2026-07-14", "author": { "@type": "Person", "name": "前端老K" } } </script> </head> <body> <!-- ✅ 9. 合理使用H1/H2层级 --> <article> <h1>HTML面试核心考点</h1> <section> <h2>语义化标签</h2> <p>正文...</p> </section> </article> <!-- ✅ 10. 图片加alt属性 --> <img src="crp-flow.png" alt="关键渲染路径流程图"> </body> </html>八、图片懒加载的3种实现方式(含原生方案)
📝 标准答案
懒加载核心原理:初始只加载视口内的图片,滚动到可视区域时再加载真实图片,节省带宽和首屏加载时间。
💻 方式一:原生loading="lazy"(最简洁)
<!-- 浏览器原生支持,兼容Chrome 77+、Firefox 75+ --> <img src="large-image.jpg" alt="大图" loading="lazy" width="800" height="600"> <iframe src="video.html" loading="lazy"></iframe> <!-- iframe也支持 -->
优点:零JS代码,浏览器自动处理。
缺点:兼容性一般(Safari 16.4+才支持),无法自定义占位图。
💻 方式二:Intersection Observer API(推荐生产)
// 封装通用懒加载类 class LazyLoader { constructor(selector = 'img[data-src]') { this.images = document.querySelectorAll(selector); this.observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; const src = img.dataset.src; if (src) { img.src = src; img.removeAttribute('data-src'); // 加载完成后添加淡入效果 img.classList.add('loaded'); } this.observer.unobserve(img); // 加载后取消观察 } }); }, { rootMargin: '50px 0px', // 提前50px开始加载 threshold: 0.01 }); this.images.forEach(img => this.observer.observe(img)); } // 销毁方法(性能优化) destroy() { this.observer.disconnect(); } } // 使用 document.addEventListener('DOMContentLoaded', () => { new LazyLoader('img[data-src]'); });<!-- HTML结构 --> <img data-src="real-image.jpg" alt="真实图片" class="lazy" width="400" height="300"> <img data-src="another.jpg" alt="另一张" class="lazy" width="400" height="300">
/* 配合占位背景 + 淡入动画 */ .lazy { background: #f0f0f0 url('placeholder.svg') center/cover no-repeat; transition: opacity 0.3s ease; opacity: 0; } .lazy.loaded { opacity: 1; }💻 方式三:滚动监听 +getBoundingClientRect(兼容性最佳)
// 兼容所有浏览器(包括IE9+) function lazyLoadByScroll() { const images = document.querySelectorAll('img[data-src]'); function checkImages() { const windowHeight = window.innerHeight || document.documentElement.clientHeight; images.forEach(img => { if (!img.dataset.src) return; // 已加载跳过 const rect = img.getBoundingClientRect(); // 判断是否进入视口(底部大于0 且 顶部小于视口高度) if (rect.bottom > 0 && rect.top < windowHeight) { img.src = img.dataset.src; img.removeAttribute('data-src'); img.classList.add('loaded'); } }); } // 节流优化(避免滚动触发过于频繁) let ticking = false; window.addEventListener('scroll', () => { if (!ticking) { window.requestAnimationFrame(() => { checkImages(); ticking = false; }); ticking = true; } }); // 初始加载 window.addEventListener('load', checkImages); } lazyLoadByScroll();🎯 面试官追问
Q:loading="lazy"和 Intersection Observer 怎么选?
A:纯展示类网站直接用
loading="lazy"最省事;复杂业务(需要占位图、加载动画、错误重试)用 Intersection Observer 手写封装,可控性更强。
九、同源策略下,跨标签页/跨窗口通信的4种方案
📝 标准答案
同源策略下,不同标签页/窗口间通信的核心前提是同源(协议+域名+端口一致)。若跨源,需用postMessage。
💻 方案一:localStorage+storage事件(同源,最常用)
// === 页面A:设置数据 === localStorage.setItem('userInfo', JSON.stringify({ name: '张三', role: 'admin' })); // === 页面B:监听storage事件(自动触发) === window.addEventListener('storage', (e) => { // 注意:只有非当前页面修改localStorage才会触发(即:A修改,B触发;B修改,A触发) if (e.key === 'userInfo') { const newData = JSON.parse(e.newValue); console.log('收到数据更新:', newData); // 更新UI document.getElementById('user-name').textContent = newData.name; } }); // ⚠️ 注意:storage事件在同一个页面内修改不会触发,只有在其他标签页修改才触发。 // 如果要当前页面也能响应,需要手动调用同步逻辑。💻 方案二:BroadcastChannel(同源,一对一频道)
// === 页面A:创建频道并发送 === const channel = new BroadcastChannel('order_channel'); channel.postMessage({ action: 'NEW_ORDER', orderId: '20260714001' }); // === 页面B:监听同一频道 === const channelB = new BroadcastChannel('order_channel'); channelB.onmessage = (e) => { if (e.data.action === 'NEW_ORDER') { console.log('新订单通知:', e.data.orderId); // 弹出提醒或刷新列表 } }; // 关闭频道(页面卸载时) channelB.close();💻 方案三:SharedWorker(同源,多标签共享线程)
// === shared-worker.js (独立文件)=== const ports = []; self.onconnect = (e) => { const port = e.ports[0]; ports.push(port); port.onmessage = (msg) => { // 广播给所有连接的标签页 ports.forEach(p => { if (p !== port) { p.postMessage(msg.data); } }); }; }; // === 页面A:连接SharedWorker === const worker = new SharedWorker('shared-worker.js'); worker.port.onmessage = (e) => { console.log('收到广播:', e.data); }; worker.port.postMessage('Hello from Page A'); // === 页面B:同样连接即可接收 === // 代码同页面A,SharedWorker会复用同一个实例💻 方案四:window.postMessage(跨源也支持)
<!-- 父页面 http://a.com/index.html --> <iframe src="http://b.com/embed.html" id="myIframe"></iframe> <script> const iframe = document.getElementById('myIframe'); iframe.onload = () => { // 向b.com发送消息(跨源) iframe.contentWindow.postMessage('Hello from a.com', 'http://b.com'); }; // 接收b.com的回信 window.addEventListener('message', (e) => { // 必须验证来源(安全!) if (e.origin === 'http://b.com') { console.log('收到回信:', e.data); } }); </script><!-- 子页面 http://b.com/embed.html --> <script> window.addEventListener('message', (e) => { if (e.origin === 'http://a.com') { console.log('父页面说:', e.data); // 回信 e.source.postMessage('Hello back', e.origin); } }); </script>🎯 面试官追问
Q:BroadcastChannel 和 SharedWorker 有什么区别?
A:BroadcastChannel轻量级,专用于同源标签页间消息广播,API简单;SharedWorker 是独立的后台线程,不仅能通信,还能执行复杂计算、维护共享状态。如果只是UI同步,选BroadcastChannel;需要共享数据/计算,选SharedWorker。
Q:postMessage跨源通信有什么安全风险?
A:必须验证
e.origin,否则恶意页面可以伪造消息窃取数据。另外,不要信任e.data中的内容,需做校验和过滤。
十、<iframe>的优缺点及安全解决方案
📝 标准答案
优点:
嵌入第三方内容(地图、视频、支付页面)。
沙箱隔离(可限制脚本、表单提交)。
微前端架构中作为子应用容器。
实现无刷新文件上传(form + iframe)。
缺点:
阻塞页面
onload事件(主页面需等iframe加载完)。内存泄漏(未销毁的iframe占用大量内存)。
SEO不友好(搜索引擎爬取困难)。
安全风险(点击劫持、恶意脚本执行)。
💻 安全最佳实践代码
<!-- ✅ 完整安全配置的iframe --> <iframe src="https://third-party.com/widget" sandbox="allow-scripts allow-same-origin" loading="lazy" referrerpolicy="no-referrer" allow="geolocation; microphone" width="100%" height="400" importance="low"> <!-- 降级方案:不支持iframe时显示 --> <p>您的浏览器不支持iframe,请访问 <a href="https://third-party.com/widget">原文链接</a></p> </iframe>
// ✅ 动态创建iframe(减少首屏阻塞) function loadLazyIframe(src, container) { const iframe = document.createElement('iframe'); iframe.src = src; iframe.sandbox = 'allow-scripts allow-same-origin'; iframe.loading = 'lazy'; iframe.setAttribute('importance', 'low'); // 监听加载完成,移除loading状态 iframe.onload = () => { iframe.classList.add('loaded'); }; // 错误兜底 iframe.onerror = () => { container.innerHTML = '<p>第三方内容加载失败</p>'; }; container.appendChild(iframe); } // 页面空闲时加载 if ('requestIdleCallback' in window) { requestIdleCallback(() => { loadLazyIframe('https://map.example.com/embed', document.getElementById('map-area')); }); } else { setTimeout(() => loadLazyIframe('...'), 2000); }/* ✅ 防点击劫持(服务端设置) */ /* HTTP响应头:X-Frame-Options: DENY 或 SAMEORIGIN */ /* 或通过meta (仅部分浏览器支持) */
// ✅ 在iframe内部防止被父页面嵌套(点击劫持防御) if (window.top !== window.self) { // 检查父页面是否同源 try { if (window.top.location.origin !== window.location.origin) { window.top.location = window.self.location; // 跳出框架 } } catch (e) { // 跨源无法访问top.location,说明被跨源嵌套,直接跳出 window.top.location = window.self.location; } }🎯 面试官追问
Q:sandbox属性有哪些常用值?
A:
allow-scripts:允许执行JS(默认禁止)。
allow-same-origin:允许与iframe同源(不设置则视为独立异源)。
allow-forms:允许表单提交。
allow-popups:允许弹窗。
allow-modals:允许alert/confirm。安全原则:按需开启,最小权限。
📌 总结与记忆口诀
| 题号 | 核心考点 | 一句话记忆 |
|---|---|---|
| 1 | 语义化标签 | header/nav/main/article/section/aside/footer |
| 2 | DOCTYPE模式 | 不写怪异模式,写了标准模式 |
| 3 | defer/async | async即下即执行,defer等解析完按序 |
| 4 | 四大存储 | Cookie小、Local永久、Session会话、Indexed无限 |
| 5 | CRP路径 | DOM+CSSOM=渲染树→布局→绘制 |
| 6 | src/href | src替换嵌,href关联连 |
| 7 | SEO优化 | title/description/语义化/alt/结构化数据 |
| 8 | 懒加载 | 原生lazy、Intersection Observer、滚动监听 |
| 9 | 跨标签通信 | localStorage、BroadcastChannel、SharedWorker、postMessage |
| 10 | iframe安全 | sandbox + loading + 防嵌套 |
面试前5分钟速记:语义结构 > DOCTYPE > 脚本加载 > 存储选型 > CRP流程 > 通信方案 > iframe安全。
🔥 下篇预告
《第2章:CSS 核心考点,布局与视觉的修罗场》
涵盖:盒模型、BFC、Flex/Grid、层叠上下文、移动端适配、CSS变量、动画性能等10道硬核原题。
觉得有用,点赞 + 收藏 + 关注,系列持续更新不迷路!
有任何题目想补充,评论区告诉我,下篇安排!