一:从输入 URL 到页面渲染 是一个经典的综合性考题,而理解 URL(统一资源定位符) 的组成是这一切的起点
1.URL 的标准组成部分

一个完整的 URL 结构如下:
scheme://host:port/path?query#fragment
URI 用字符串标识某一互联网资源,而URL 表示资源的地点(互 联网上所处的位置)。可见URL是URI 的子集。

URI 和 URL 的区别?
-
URI (Uniform Resource Identifier) 是统一资源标识符,是一个大概念。
-
URL (Uniform Resource Locator) 是统一资源定位符,它不仅标识资源,还提供了找到资源的方式(比如协议)。可以理解为 URL 是 URI 的子集。
为什么 URL 中有些字符会被转义(如 %20)?
- URL 只能使用 ASCII 字符集。特殊字符(如空格、中文)必须通过
encodeURI或encodeURIComponent进行编码。
1. 什么是"同源"?
根据我们刚刚讨论的 URL 组成,只有当两个 URL 的 协议 (Protocol) 、域名 (Domain) 和 端口 (Port) 均相同时,才被称为同源。
-
https://a.com/page1和https://a.com/page2------ 同源 -
http://a.com和https://a.com------ 跨域(协议不同) -
https://a.com和https://b.com------ 跨域(域名不同) -
https://a.com和https://a.com:8080------ 跨域(端口不同)
2. 常见的跨域解决方案
在面试中,你通常需要给出以下三种最核心的方案:
A. CORS (Cross-Origin Resource Sharing) - 最主流
这是 W3C 标准,由后端通过设置 HTTP 响应头来告诉浏览器:"我允许这个源访问我的资源"。
-
核心响应头:
Access-Control-Allow-Origin: *(或指定的域名)。 -
简单请求 vs 预检请求:对于复杂请求(如
PUT、DELETE或自定义 Header),浏览器会先发一个OPTIONS方法的请求,称为预检请求。
B. JSONP (JSON with Padding) - 兼容老旧浏览器
利用了 <script> 标签不受同源策略限制的特性。
-
限制:仅支持 GET 请求。
-
原理代码实现:
javascript/** * 模拟一个简单的 JSONP 实现 */ function jsonp({ url, params, callbackName }) { return new Promise((resolve) => { // 1. 创建 script 标签 const script = document.createElement('script'); // 2. 将回调函数挂载到 window,供服务端调用 window[callbackName] = function(data) { resolve(data); document.body.removeChild(script); // 善后工作 delete window[callbackName]; }; // 3. 构建 URL,带上 callback 参数 const query = { ...params, callback: callbackName }; const queryString = Object.keys(query).map(key => `${key}=${query[key]}`).join('&'); script.src = `${url}?${queryString}`; // 4. 插入页面,触发请求 document.body.appendChild(script); }); } // 使用示例 // jsonp({ url: 'http://api.test.com/data', callbackName: 'handleRes' }).then(res => console.log(res));C. Proxy (代理) - 开发环境最常用
通过"中间人"绕过浏览器限制。浏览器访问同源的代理服务器,代理服务器再去请求目标服务器(服务器之间没有同源策略)。
-
前端构建工具配置(如 Vite/Webpack):
javascript
// vite.config.js 示例
export default {
server: {
proxy: {
'/api': {
target: 'http://backend-api.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
}
3. 面试官深挖:Cookie 如何在跨域中携带?
这是一个高频追问点。默认情况下,跨域请求不带 Cookie。
-
前端 :在 Ajax/Fetch 请求中设置
withCredentials = true。 -
后端 :设置响应头
Access-Control-Allow-Credentials: true。 -
注意 :此时
Access-Control-Allow-Origin不能 设为通配符*,必须指定具体域名。
总结建议
在面试时,你可以这样总结:"跨域是浏览器的安全屏障,解决它的核心思路要么是让服务器明确许可(CORS),要么是利用标签特性(JSONP),要么是避开浏览器环境(Proxy)。"
2. DNS 解析过程

1. DNS 解析过程:将域名变为 IP 地址
当你输入
www.example.com时,计算机并不认识这个字符串,它需要通过 DNS(域名系统)找到对应的 IP 地址。
详细步骤(递归 + 迭代):
-
浏览器缓存/操作系统缓存 :首先检查浏览器自身是否有该域名的解析记录,如果没有,再检查操作系统的
hosts文件。 -
本地 DNS 服务器(LDNS):通常是你接入的网络服务商(ISP)。
-
根域名服务器(Root Nameserver) :LDNS 如果没有缓存,会去问根服务器:"我知道
.com在哪吗?"。 -
顶级域名服务器(TLD Nameserver) :根服务器指向
.com服务器,LDNS 再去问.com服务器:"example.com在哪?"。 -
权威域名服务器(Authoritative Nameserver) :最后找到负责
example.com的权威服务器,拿到具体的 IP 地址,返回给浏览器并缓存。
2. TCP/IP 分层管理:数据是如何打包的?
在建立连接前,我们要理解数据是怎么通过网络栈传输的。TCP/IP 通常分为四层(或 OSI 七层模型,面试常考四层):

1.应用层:
应用层决定了向用户提供应用服务时通信的活动。 TCP/IP协议族内预存了各类通用的应用服务。比如, FTP (File Transfer Protocol, 文件传输协议)和DNS (Domain Name System, 域名系统)服务就是其中两类。 HTTP 协议也处于该层。
2.传输层:
传输层对上层应用层,提供处于网络连接中的两台计算机之间的 数据传输。 在传输层有两个性质不同的协议: TCP (Transmission Control Protocol,传输控制协议)和UDP (User Data Protocol,用户数据 报协议)。
3.网络层(又名网络互连层)
网络层用来处理在网络上流动的数据包。数据包是网络传输的最小 数据单位。该层规定了通过怎样的路径(所谓的传输路线)到达对 方计算机,并把数据包传送给对方。 与对方计算机之间通过多台计算机或网络设备进行传输时,网络层 所起的作用就是在众多的选项内选择一条传输路线。
4.链路层(又名数据链路层,网络接口层)
用来处理连接网络的硬件部分。包括控制操作系统、硬件的设备驱 动、NIC (Network Interface Card,网络适配器,即网卡),及光纤等物理可见部分(还包括连接器等一切传输媒介)。硬件上的范畴 均在链路层的作用范围之内。
图解


3. TCP 三次握手:确保双方都有收发能力
拿到 IP 后,客户端需要与服务器建立 TCP 连接。为什么是三次?因为要确保双向通信链路都是通畅的。

- 第一次握手 :客户端发送
SYN(Synchronize) 包,序列号为x。
状态:客户端进入 SYN_SENT。服务器确认了"客户端发送能力正常"。
- 第二次握手 :服务器返回
SYN+ACK(Acknowledgment) 包,序列号y,确认号x+1。
状态:服务器进入 SYN_RCVD。客户端确认了"服务器接收和发送能力都正常"。
- 第三次握手 :客户端发送
ACK包,序列号x+1,确认号y+1。
状态:双方进入 ESTABLISHED。服务器确认了"客户端接收能力正常"。
4. 四次挥手的详细过程
想象客户端(Client)主动发起断开请求:
- 第一次挥手 (FIN) :客户端发送一个
FIN(Finish) 报文,用来告诉服务器:"我没有数据要发给你了,我要关闭发送通道"。
状态:客户端进入 FIN_WAIT_1 状态。
- 第二次挥手 (ACK) :服务器收到
FIN,回复一个ACK。意思是:"收到了,但我可能还有数据没发完,你等我一下"。
状态:服务器进入 CLOSE_WAIT,客户端进入 FIN_WAIT_2。此时连接处于"半关闭"状态。
- 第三次挥手 (FIN) :服务器处理完最后的数据,向客户端发送
FIN。意思是:"好了,我也发完了,我也要关了"。
状态:服务器进入 LAST_ACK。
- 第四次挥手 (ACK) :客户端收到服务器的
FIN,回复最后一个ACK。意思是:"收到,祝好"。
状态:客户端进入 TIME_WAIT 状态,等待 2MSL 后彻底关闭。服务器收到 ACK 后立即 CLOSED。
面试高频追问:为什么会有 TIME_WAIT?
这是四次挥手最常被问到的技术细节。客户端在发送完最后一个 ACK 后,并不会立即关掉连接,而是要等待 2MSL(Maximum Segment Lifetime,报文最大生存时间)。
原因有两个:
-
确保最后的 ACK 能够到达服务器 :如果最后一个 ACK 丢了,服务器会超时重传第三次的
FIN。客户端只有在TIME_WAIT期间才能重发 ACK。 -
防止"已失效的请求"干扰:等待足够长的时间,让本次连接产生的所有报文都在网络中消失。这样下次建立相同 IP 和端口的连接时,就不会收到上一次连接残留的旧数据。
代码与性能层面:CLOSE_WAIT 过多怎么办?
作为前端或全栈开发者,如果监控发现服务器出现大量 CLOSE_WAIT 状态,通常是因为程序 Bug。
-
原因 :对方发了
FIN,但你的代码没有调用close()方法关闭 Socket(例如后端连接池没释放,或者长连接逻辑出错)。 -
后果:会占用大量文件描述符,导致新连接无法建立。
5.HTTP 缓存策略
HTTP 缓存就是:浏览器把请求过的资源存起来,下次用的时候直接从本地拿,不再麻烦服务器**。**
1. 缓存的整体流程
当浏览器发起请求时,它会遵循以下逻辑:
-
先看强缓存:如果命中了,直接用,不发请求到服务器。
-
再看协商缓存:如果没有强缓存或已过期,就带着"凭证"去问服务器:"我这资源还能用吗?"。
-
返回结果:
-
服务器说"没变":返回 304 Not Modified,浏览器继续用本地的。
-
服务器说"变了":返回 200 OK 和新内容
-
2. 强缓存 (Strong Cache)
特点 :不需要发送 HTTP 请求,直接从内存 (
from memory cache) 或磁盘 (from disk cache) 读取。
关键响应头:
-
Expires (HTTP/1.0):
-
值是一个绝对时间(如
Expires: Wed, 21 Oct 2025 07:28:00 GMT)。 -
缺点:受限于客户端本地时间,如果用户改了系统时间,缓存会失效。
-
-
Cache-Control (HTTP/1.1) - 推荐:
-
使用相对时间,优先级高于
Expires。 -
常用指令:
-
max-age=3600:缓存 1 小时。 -
no-cache:不使用强缓存,直接进入协商缓存阶段。 -
no-store:完全不缓存,每次都要重新下载。
-
-
3. 协商缓存 (Negotiation Cache)
特点:必须发请求到服务器,由服务器决定是否使用缓存。

4.模拟代码场景:如何设置缓存?
如果你在写一个 Node.js (Express) 后端,你可以这样控制缓存:
javascript
const express = require('express');
const app = express();
app.get('/static/logo.png', (req, res) => {
// 设置强缓存:1年 (单位:秒)
res.setHeader('Cache-Control', 'public, max-age=31536000');
// 或者设置协商缓存 (Express 默认会自动处理 ETag)
// res.setHeader('Cache-Control', 'no-cache');
res.sendFile(__dirname + '/logo.png');
});
app.listen(3000);
5. 面试官杀手锏:用户操作对缓存的影响
这是很多候选人会忽略的细节:
-
地址栏回车 / 链接跳转:有效,强缓存和协商缓存都正常工作。
-
F5 刷新 :失效 。浏览器会在请求头带上
Cache-Control: max-age=0,跳过强缓存,直接发起协商缓存。 -
Ctrl + F5 (强制刷新) :全部失效。跳过所有缓存,直接从服务器拉取最新的。
总结建议
在面试中谈论缓存时,你可以顺便带出 "大前端部署实践":
"通常我们会给 HTML 设置
no-cache(走协商缓存),而给静态资源(JS/CSS/图片)设置超长强缓存。为了更新这些资源,我们会给文件名加上 Content Hash。这样只要代码变了,文件名就变,浏览器就会请求新文件,而没变的文件依然能秒开。"
6.状态码
1. 状态码分类概览

2xx - 成功
-
200 OK:请求成功,有返回内容。
-
204 No Content :请求成功,但没有响应主体 (常用于
OPTIONS预检请求或删除操作)。 -
206 Partial Content :客户端发送了范围请求(
RangeHeader),服务器成功返回了部分内容。常用于断点续传或大视频分段加载。
3xx - 重定向
-
301 Moved Permanently:永久重定向。浏览器会自动缓存新地址,下次直接跳新地址。
-
302 Found:临时重定向。下次访问还是会请求原地址。
-
304 Not Modified :重点! 协商缓存命中,服务器告诉浏览器:"东西没变,你直接用本地缓存吧"。
4xx - 客户端错误
-
400 Bad Request:请求语法错误(比如 JSON 格式写错了)。
-
401 Unauthorized:未授权,需要身份验证(比如没登录)。
-
403 Forbidden :服务器理解请求,但拒绝执行(比如你有登录,但没有管理员权限访问该页面)。
-
404 Not Found:资源不存在。
-
405 Method Not Allowed :方法不支持(比如接口只准
POST,你用了GET)。
5xx - 服务器错误
-
500 Internal Server Error:后端代码报错了。
-
502 Bad Gateway:充当网关或代理的服务器(如 Nginx)尝试执行请求时,从上游服务器接收到无效响应。
-
503 Service Unavailable:服务器超载或停机维护。
-
504 Gateway Timeout:网关超时,上游服务器没在规定时间内返回数据。
2.面试官进阶:301 和 302 对 SEO 的影响?
-
301 (永久):搜索引擎在抓取时会把旧地址的权重(PR值)转移到新地址。如果你换了新域名,一定要做 301。
-
302 (临时):权重不会转移。如果滥用 302,可能会被搜索引擎判定为 URL 劫持。
总结建议
当面试官问你状态码时,不仅要说出数字含义,最好能结合实际业务场景。比如谈到
206聊聊视频大文件下载,谈到304聊聊刚才说的 HTTP 缓存,谈到401/403聊聊权限控制。
7.HTTP和 HTTPS的区别
1.核心区别汇总

2. HTTPS 的"安全"是如何实现的?
HTTPS 并不是一种新的协议,而是 HTTP + SSL/TLS。它主要解决了 HTTP 的三个安全问题:
-
机密性(Encryption):防止数据被中间人窃听。
-
完整性(Integrity):防止数据在传输过程中被篡改。
-
身份认证(Authentication):确认你访问的网站确实是"官宣"的那个,而不是钓鱼网站。
3. HTTPS 的握手过程(核心考点)
这是面试官最喜欢问的细节。它结合了对称加密和非对称加密的优点。
-
客户端请求:客户端(浏览器)向服务器发起 HTTPS 请求,连接到 443 端口,发送支持的加密算法列表。
-
服务器响应 :服务器选择一套加密算法,并发送自己的数字证书(包含服务器公钥)。
-
客户端验证:
-
浏览器检查证书是否过期、颁发机构是否可信。
-
如果验证通过,客户端生成一个随机数(预主密钥)。
-
-
非对称加密传输密钥 :客户端用服务器的公钥加密这个随机数,发给服务器。
-
服务器解密 :服务器用自己的私钥解密,得到这个随机数。
-
对称加密传输数据 :现在双方都有了这个随机数,它将作为对称加密的密钥。此后的所有数据传输都使用这个随机数进行加密。
为什么这样设计?
非对称加密 (公钥/私钥)虽然安全,但速度慢。
对称加密 (一个密钥)速度快,但密钥传输不安全。
结论:用非对称加密来安全地传输对称加密的密钥,然后用对称加密来传数据。
4. 代码层面:前端需要做什么?
作为前端开发者,你不需要编写加密算法,但你需要知道:
-
Mixed Content 警告:如果你的 HTTPS 页面中引用了 HTTP 的静态资源(如图片、脚本),浏览器会报错或拦截。
-
HSTS (HTTP Strict Transport Security):一种安全策略,强制浏览器只使用 HTTPS 与服务器通信。
javascript
// 在 Node.js (Express) 中开启 HTTPS 的简单示例
const https = require('https');
const fs = require('fs');
const express = require('express');
const options = {
key: fs.readFileSync('server.key'), // 私钥
cert: fs.readFileSync('server.crt') // 证书
};
const app = express();
https.createServer(options, app).listen(443, () => {
console.log('HTTPS Server running on port 443');
});
5. 面试官可能追问:什么是中间人攻击(MITM)?
如果黑客伪造了证书,而用户忽略了浏览器的安全警告点击了"继续访问",黑客就可以解密你的数据。这就是为什么证书的有效性验证至关重要。
二:浏览器渲染原理
1. 渲染模式的分水岭:后端返回了什么?
1.服务端渲染 (SSR - Server Side Rendering)
-
后端返回:一个包含了完整 DOM 结构的 HTML 字符串。
-
浏览器起点:拿到 HTML 后立即开始解析并显示内容。
-
特点:SEO 友好,首屏加载快(白屏时间短)。
-
现代框架:Next.js (React), Nuxt.js (Vue)。
2.客户端渲染 (CSR - Client Side Rendering)
-
后端返回 :一个几乎为空的 HTML 壳子 (通常只有一个
<div id="app"></div>)和一堆 JS 文件。 -
浏览器起点:必须等待 JS 下载并执行完毕后,由 JS 动态创建 DOM 节点。
-
特点:用户体验流畅(页面切换无刷新),但首屏可能较慢。
-
现代框架:普通的 Vue-cli 或 Create-React-App 项目。
2. 浏览器渲染的详细流水线 (The Critical Rendering Path)
无论数据是怎么来的,一旦浏览器拿到了 HTML、CSS 和 JS,就会进入关键渲染路径。这是面试中最核心的流程:
第一步:构建对象模型
-
构建 DOM 树 :浏览器将 HTML 字节流解析为一个个令牌(Tokens),然后转换成节点,最后构建成 DOM Tree。
-
构建 CSSOM 树:解析所有的 CSS(包括外部文件和内联样式),构建出 CSSOM Tree。
第二步:合并成渲染树 (Render Tree)
浏览器将 DOM 和 CSSOM 合并。
注意 :
display: none的节点不会出现在渲染树中,但visibility: hidden的节点会。
第三步:布局 (Layout / Reflow)
计算每个节点在屏幕上的确切位置和大小。想象成在一个白纸上画方块,确定坐标。
第四步:绘制 (Paint / Repaint)
将节点的像素信息(颜色、边框、阴影等)绘制到屏幕上。
第五步:合成 (Composite)
如果页面有复杂的层级(如 3D 转换、Canvas、video),浏览器会将它们分层处理,最后合成到一起。
3. 为什么 JS 会阻塞渲染?
原理 :默认情况下,HTML 解析器遇到
<script>标签时会暂停,去下载并执行 JS。因为 JS 可能会操作 DOM 或修改 CSS(导致前面的工作白费)
解决方案:
-
defer:脚本异步下载,等待 HTML 解析完成后执行。
-
async:脚本异步下载,下载完立即执行(可能中断解析)
4. 面试必考:重排 (Reflow) 与 重绘 (Repaint)

5. 总结与扩展
从后端返回 HTML 开始,到屏幕显示出图像,浏览器经历了一个非常复杂的流水线。
-
SSR 提前在服务器做好了"构建对象模型"的一大部分工作。
-
CSR 把这些工作全丢给了浏览器的 JS 引擎。
三:V8引擎
V8 引擎(Chrome 和 Node.js 的核心)之所以快,是因为它摒弃了传统的"解释执行",采用了 JIT (Just-In-Time) 即时编译技术。
核心执行流程:
-
解析(Parser) :将源代码转为 抽象语法树(AST)。
-
词法分析:把代码拆成一个个不可再分的词(Tokens)。
-
语法分析:根据语法规则把 Tokens 组成树状结构。
-
-
解释(Ignition):解释器将 AST 转为字节码(Bytecode)并开始执行。
- 字节码比机器码轻量,可以跨平台运行。
-
优化(TurboFan) :编译器会标记"热点代码"(执行次数很多的函数),将其直接编译为高效的机器码。
-
去优化(Deoptimization):如果热点代码的变量类型发生了变化(例如原本传数字,后来传了字符串),V8 会把机器码退回到字节码。
面试加分点 :为什么 V8 提倡写"类型确定"的代码? 因为类型一旦变化,就会触发 Deoptimization,导致性能陡降。这也是为什么 TypeScript 在大型项目中能间接提升性能(通过规范类型减少 V8 的猜疑)。
四: 事件循环 (Event Loop):异步调度的灵魂
事件循环是 JS 实现非阻塞 I/O 的核心。由于 JS 是单线程的,它必须通过一个机制来协调同步代码和异步任务。
任务分类:
-
宏任务 (Macrotask) :
script(整体代码),setTimeout,setInterval,I/O,UI rendering。 -
微任务 (Microtask) :
Promise.then,MutationObserver,process.nextTick。
执行顺序 (Event Loop Tick)
-
执行一个宏任务(最开始是同步代码)。
-
执行过程中如果遇到微任务,放入微任务队列。
-
当前宏任务执行完后,立即清空所有的微任务队列。
-
(关键点):更新渲染(Update Rendering)。
-
检查是否有 Web Worker 任务,开始下一个宏任务。
代码实战分析:
javascript
console.log('1'); // 同步
setTimeout(() => {
console.log('2'); // 宏任务
}, 0);
Promise.resolve().then(() => {
console.log('3'); // 微任务
});
console.log('4'); // 同步
// 输出顺序:1 -> 4 -> 3 -> 2
解释:1 和 4 先入栈执行。执行完后,检查微任务队列发现 3,执行 3。最后开启下一个循环,执行宏任务 2。
五: 浏览器缓存与渲染结合:Service Worker
如果说 HTTP 缓存是"自动挡",那么 Service Worker 就是"手动挡"。它是运行在浏览器后台的独立线程 。Service Worker 的地位:它充当了浏览器与网络之间的代理服务器。它可以拦截网络请求,并根据策略决定是走网络、走缓存,还是直接返回一段自定义内容。
关键特性:
-
离线能力:即使没网,也能通过缓存加载页面(PWA 的核心)。
-
推送通知:可以在后台接收服务器消息。
-
不能直接操作 DOM :必须通过
postMessage与主线程通信。
生命周期与缓存结合:
-
Install 阶段:通常用来预缓存静态资源(App Shell)。
-
Activate 阶段:清理旧缓存。
-
Fetch 阶段:拦截请求,实现缓存优先或网络优先策略。
javascript
// service-worker.js 示例
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
// 如果缓存命中,直接返回;否则走网络请求
return response || fetch(event.request);
})
);
});
总结:三者的联动
-
V8 决定了 JS 代码运行的快慢。
-
Event Loop 决定了任务执行的时机,以及是否会卡住 UI 渲染。
-
Service Worker 决定了资源加载的来源,是渲染流水线的"物料补给站"。