跳到内容
当前位置:首页>面试真题>大厂前端面试真题(二)

大厂前端面试真题(二)

  • 2026-09-19 17:33:20
大厂前端面试真题(二)

1. HTTP 和 WebSocket 的区别与适用场景

核心区别:

HTTP:基于请求-响应模式,无状态协议,每次请求都要建立新连接(HTTP/1.1有keep-alive但本质仍是请求-响应)

适用场景:

  • 网页浏览、资源加载

  • RESTful API调用

  • 文件上传下载

WebSocket:全双工通信协议,建立连接后双方可随时主动发送数据

适用场景:

  • 实时聊天应用

  • 在线协作编辑

  • 股票行情实时推送

  • 多人在线游戏

简单比喻:HTTP像是打电话,每次说完挂断;WebSocket像是微信语音通话,接通后随时可以说话。

可引申:TCP和UDP区别?TCP挥手为什么要四次?TCP的拥塞避免算法是什么?http2.0和http3.0特点?https加密流程是什么?(内容较多,后续单独梳理)

2. SSE(Server-Sent Events)及其特点

SSE允许服务器主动向客户端推送数据,基于HTTP协议实现。

核心特点:

  • 单向通信(服务器→客户端)

  • 基于HTTP,兼容性好

  • 自动重连机制

  • 轻量级,使用简单

3. 选择SSE而非WebSocket的场景

适合SSE的场景:

  • 只需要服务器向客户端推送数据(如新闻推送、AI回答)

  • 需要利用HTTP现有的基础设施(如身份验证、代理)

  • 客户端主要是现代浏览器(IE不支持SSE)

  • 对带宽敏感,SSE开销更小

4. 常见HTTP状态码有哪些

2xx 成功:

  • 200 OK:请求成功

  • 201 Created:创建成功(如新建资源)

  • 204 No Content:成功但无返回内容

3xx 重定向:

  • 301 Moved Permanently:永久重定向

  • 302 Found:临时重定向

  • 304 Not Modified:资源未修改(缓存相关)

4xx 客户端错误:

  • 400 Bad Request:请求格式错误

  • 401 Unauthorized:需要身份验证

  • 404 Not Found:资源不存在

5xx 服务器错误:

  • 500 Internal Server Error:服务器内部错误

  • 502 Bad Gateway:网关错误

  • 503 Service Unavailable:服务不可用

5. 强缓存与协商缓存

强缓存:

直接从本地缓存读取,不向服务器发送请求

通过响应头实现:

  • Cache-Control: max-age=3600(相对时间)

  • Expires: Wed, 21 Oct 2025 07:28:00 GMT(绝对时间)

优点

  • 零网络请求:缓存有效期内,完全不与服务器通信,速度极快。

  • 极大减轻服务器压力:适用于公共库、静态资源等变化极少的内容。

缺点

  • 版本管理难题:资源更新后,用户可能因本地强缓存而无法及时获取最新版本。通常需要通过修改文件名(如添加哈希 style.abcd1234.css)或URL参数来“强制”更新。

协商缓存:

每次请求都向服务器验证资源是否过期

通过请求头/响应头实现:

    • Last-Modified / If-Modified-Since

    • ETag / If-None-Match

优点

  • 确保内容新鲜度:每次都会与服务器确认,总能获取到最新的资源或确认使用缓存。

  • 解决版本问题:无需修改文件名,服务器对比标识即可决定返回304还是新资源。

缺点

  • 至少一次网络通信:即使返回304,也仍有一次HTTP请求的往返开销,速度不如强缓存。

6. Vue3 和 React 组件通信方式有哪些

Vue3 常用方式:

  • Props / Emits(父子组件)

  • Provide / Inject(跨层级)

  • Event Bus(mitt库,Vue3已移除内置EventBus)

  • Pinia / Vuex(状态管理)

  • v-model 双向绑定

React 常用方式:

  • Props 传递

  • Context API(跨层级)

  • Redux / Mobx / Zustand(状态管理)

  • 自定义Hooks

  • 回调函数传递

7. SSR 与 SSG 的区别

SSR(服务端渲染):

每次请求时在服务器生成HTML

适合:动态内容、SEO要求高、个性化页面

缺点:服务器压力大,TTFB时间可能较长

SSG(静态站点生成):

构建时生成静态HTML文件

适合:博客、文档、营销页面

优点:访问速度快,部署简单

缺点:不适合频繁更新的内容

现代方案:Next.js、Nuxt.js支持混合渲染,不同页面可采用不同策略。

8. 浏览器存储三剑客对比

特性
Cookie
localStorage
sessionStorage
存储大小
4KB左右
5-10MB
5-10MB
有效期
可设置过期时间
永久(除非手动清除)
会话结束(标签页关闭)
作用域
同源(可设domain)
同源
同源且同标签页
通信方式
每次请求自动携带
不自动发送
不自动发送
主要用途
身份验证、个性化
持久化数据
临时会话数据

9. localStorage 存 Token 的风险与解决方案

风险:

  • XSS攻击可直接读取localStorage

  • Token泄露等于账户被盗

  • 无法设置HttpOnly保护

更安全方案:

1. HttpOnly Cookie(推荐)

// 服务器设置Set-Cookie: token=xxx; HttpOnlySecureSameSite=Strict

2. 内存存储 + 短期Token

  • Token只在内存中存在

  • 配合refresh token机制

3. 浏览器安全存储API

  • 如Credential Management API(仍在发展中)

10. 移动端适配方案对比

1. rem方案(已逐渐淘汰)

原理:根据根元素font-size动态计算

优点:兼容性好

缺点:需要JS计算,不如CSS方案纯粹

2. vw/vh方案(当前主流)

优点:纯CSS实现,性能好

缺点:低版本浏览器兼容问题

3. 响应式布局

使用媒体查询

适合:官网、管理后台

不适合:复杂移动端H5

4. 混合方案

// 实际项目中常用混合方案@functionvw($px{  @return($px 375) * 100vw;}.container {  padding: vw(15);  @media (max-width768px) {    // 移动端特殊样式  }}

11. 跨域问题与解决方案

什么是跨域?浏览器同源策略限制:协议、域名、端口任一不同即跨域。

常见解决方案:

1. CORS(推荐)

// 服务器设置响应头Access-Control-Allow-Origin'https://yourdomain.com'Access-Control-Allow-Credentialstrue

2. 开发环境代理

// webpack配置devServer: {  proxy: {    '/api': {      target'http://backend:3000',      changeOrigintrue    }  }}

3. JSONP(仅限GET)

function handleResponse(data) {  console.log(data);}// 动态创建script标签const script = document.createElement('script');script.src = 'http://api.com/data?callback=handleResponse';

4. Nginx反向代理

// nginxlocation /api/ {  proxy_pass http://backend-server/;  proxy_set_header Host $host;}

算法题:爬楼梯问题

解题思路:

到达第n阶,只能从第n-1阶或第n-2阶过来

所以动态转移方程:dp[n] = dp[n-1] + dp[n-2]

方法一:动态规划 + 斐波那契数列

// 斐波那契数列思想function climbStairs(n) {  if (n <= 2return n;  // dp[i]表示爬到第i阶的方法数  const dp = new Array(n + 1);  dp[1] = 1;  // 1阶:1种方法  dp[2] = 2;  // 2阶:2种方法(1+1或2)  for (let i = 3; i <= n; i++) {    // 状态转移方程    dp[i] = dp[i - 1] + dp[i - 2];  }  return dp[n];}

方法二:动态规划 + 滚动数组

// 滚动数组,空间优化function climbStairsOptimized(n) {  if (n <= 2return n;  let prev1 = 1;  // dp[i-1]  let prev2 = 2;  // dp[i-2]  for (let i = 3; i <= n; i++) {    const current = prev1 + prev2;    prev1 = prev2;    prev2 = current;  }  return prev2;}