Skip to content

网络与浏览器 ​

1. WebSocket 和 SSE 的区别 ​

  1. 通信方向不同:WebSocket 支持双方随时发消息;SSE 主要是服务器持续向浏览器推送,浏览器提交数据通常另发 HTTP 请求。
  2. 协议不同:WebSocket 使用 ws:// 或加密的 wss://;SSE 基于 HTTP,响应类型为 text/event-stream。
  3. 数据不同:WebSocket 支持文本和二进制,JSON 要自行序列化;SSE 传 UTF-8 文本,也能在文本里放 JSON。
  4. 重连不同:浏览器原生 EventSource 支持自动重连;原生 WebSocket 通常要自己实现重连、退避和心跳。连接都可能被断开,不是“一次握手永久通信”。
  5. 场景不同:聊天、游戏、协同编辑适合 WebSocket;通知、日志、AI 文本流等服务端推送场景可以考虑 SSE。
  6. 注意客户端限制:原生 EventSource 主要使用 GET,不能随意设置请求头;需要 POST 或自定义鉴权头时,可以用 fetch 读取流,但事件解析和重连要另外实现。

参考:WebSocket、EventSource。

2. WebSocket 怎么连接?有哪些重要属性? ​

  1. 创建连接:使用 new WebSocket(url),随后等待 open 事件,不能创建后立即假设连接成功。
  2. 收发消息:连接打开后用 send() 发送,在 message 事件中读取 event.data。收到 JSON 文本时要处理解析失败。
  3. 处理异常:监听 error 和 close;重连使用逐渐延长的等待时间,避免故障时大量客户端一起重试。
  4. 重要属性:readyState 表示连接状态;bufferedAmount 是排队等待发送的字节数;binaryType 控制接收二进制数据的形式。
  5. 四种状态:CONNECTING 是连接中,OPEN 是可通信,CLOSING 是关闭中,CLOSED 是已关闭。
  6. 业务可靠性自己设计:断线期间可能丢消息,重要消息需要编号、确认、补拉和去重。浏览器原生 API 不提供自动流量背压,消息过多时要限流或合并。
  7. 离开页面要清理:关闭不再需要的连接、心跳和重连定时器。服务端还要验证用户身份和请求来源,不能只依赖 CORS。
js
const socket = new WebSocket('wss://example.com/socket'); // 替换为实际服务
socket.addEventListener('open', () => {
  socket.send(JSON.stringify({ type: 'subscribe', channel: 'news' }));
});
socket.addEventListener('message', event => {
  console.log(event.data);
});
socket.addEventListener('error', () => console.error('连接异常'));
socket.addEventListener('close', () => console.log('连接已关闭'));
// 组件卸载时调用 socket.close(),并清理业务定时器。

3. MCP、A2A、AG-UI 是什么?为什么需要它们? ​

  1. MCP 连接工具和数据:让 AI 应用按统一约定使用搜索、文件、业务接口等能力,减少每接一个系统就重写一套对接代码。
  2. A2A 连接不同 Agent:让不同系统的 Agent 发现彼此能力、交接任务和交换结果,不需要暴露对方全部内部实现。
  3. AG-UI 连接 Agent 和用户界面:约定文本流、工具状态、应用状态等事件,让前端知道任务进行到了哪一步。
  4. 三者解决不同层面的问题:可以理解成“调用工具、Agent 协作、与界面交互”,并不互相替代,也不要求项目同时使用。
  5. 协议不等于权限和业务实现:接入后仍然需要鉴权、参数校验、错误处理和权限控制,也不会自动让 AI 的结果正确。

参考:MCP、A2A、AG-UI。

4. 如何在页面上渲染数学公式? ​

  1. 先确定输入格式:常见是 LaTeX 字符串,例如 x^2 + y^2 = z^2,不能直接当普通 HTML 显示成公式。
  2. 使用专门的渲染库:常见有 KaTeX 和 MathJax,根据公式支持范围、性能和无障碍要求选择。
  3. 和 Markdown 配合:配置数学公式插件,约定行内公式和块级公式的分隔符;只写 $...$ 不代表浏览器会自动渲染。
  4. 流式内容要等边界完整:收到半个公式时先保留文本,等公式结束再渲染,避免频繁报错和抖动。
  5. 处理异常和安全:非法公式显示原文或错误提示;限制输入长度,谨慎开放可加载外部资源或输出 HTML 的扩展功能。

参考:KaTeX 配置选项。

5. OPTIONS 是什么?什么时候触发预检? ​

  1. OPTIONS 是 HTTP 方法:用于询问服务端支持哪些通信选项。CORS 预检会用它,但不是所有 OPTIONS 请求都是预检。
  2. 不是所有跨域请求都有预检:满足 CORS 安全列表条件的请求可以直接发送;跨域使用 PUT、DELETE、自定义请求头或 application/json 等通常会触发预检。
  3. 浏览器先询问服务器:预检包含 Origin、Access-Control-Request-Method,必要时包含 Access-Control-Request-Headers。
  4. 服务器返回允许范围:使用 Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers 等响应头。
  5. 不要记错字段:请求头是单数的 Access-Control-Request-Method,不是 Methods;没有标准的 Access-Control-Request-Credentials 预检请求头。
  6. 预检成功才继续实际请求:实际响应仍要满足 CORS 条件。预检许可可以用 Access-Control-Max-Age 缓存,但受浏览器限制。

参考:CORS 预检。

6. CORS 是什么?解决什么问题? ​

  1. CORS 是跨源资源共享:服务器用响应头告诉浏览器,哪些来源的脚本可以读取自己的响应。
  2. 核心是服务端允许:例如允许指定前端域名。前端自己加一个 Access-Control-Allow-Origin 请求头不能解决问题。
  3. 携带 Cookie 要配合配置:跨源 fetch 通常需要 credentials: 'include',服务端允许凭证,并返回具体允许的 Origin,不能用 *。
  4. Cookie 规则还会独立生效:SameSite、Secure、第三方 Cookie 限制等,可能导致 Cookie 仍然不发送。
  5. CORS 不是鉴权:不阻止非浏览器客户端请求,也不能单独防止 CSRF。有的请求已经到达服务器,只是浏览器不让脚本读取响应。

参考:MDN CORS。

7. 怎么理解跨域?怎么解决? ​

  1. 比较协议、主机和端口:三者有一个不同就是不同源。a.example.com 和 b.example.com 也不同源;路径不同不算跨域。
  2. 浏览器限制部分跨源访问:主要保护数据读取和页面互相访问,不是说所有跨源资源都不能加载。
  3. 常见方案是 CORS 或反向代理:后端配置 CORS;或者由同源服务转发 API 请求,浏览器只访问同源入口。
  4. 开发代理只解决开发环境:Vite 的开发代理不会自动变成线上代理,生产环境需要单独配置网关或服务端。
  5. no-cors 不是万能解决办法:响应通常变成不透明响应,JavaScript 读不到正文;关闭浏览器安全设置也不是正式方案。
  1. Cookie 是浏览器保存的小段数据:符合域名、路径和安全规则时,会随 HTTP 请求自动发送。
  2. Session 是服务端会话机制:服务端保存用户会话数据,浏览器通常通过 Cookie 携带一个 session ID,让服务端找到对应会话。
  3. 两者常常配合使用:Cookie 可以存会话标识,也能存其他小数据;Session 不等于 Cookie 本身。
  4. 过期需要两边考虑:Cookie 有过期设置,服务端 Session 也有有效期;删除 Cookie 不一定立即删除服务端记录。
  5. 安全属性要理解:HttpOnly 阻止脚本直接读取 Cookie,Secure 限制通过 HTTPS 发送,SameSite 控制部分跨站携带行为。它们不能替代服务端鉴权。
  6. 多台服务要共享会话或统一校验:否则请求换一台机器,就可能找不到登录信息。

9. LocalStorage 存状态需要考虑什么?什么时候取不到? ​

  1. 只存字符串:对象需要序列化,读取时处理 JSON 格式错误,并校验字段结构。
  2. 按 Origin 隔离:协议、域名或端口变了,就不是同一个存储空间;同源不同路径通常共享。
  3. 没有自动过期机制:业务需要保存过期时间,并在读取时判断;数据版本变化时也要迁移或清理。
  4. 它是同步 API:频繁写入大对象会阻塞主线程,不适合大量数据和高频更新。
  5. 可能取不到或直接抛错:用户清理数据、键不存在、隐私模式、存储被禁用或容量不足都会影响使用。SSR 中还可能根本没有 window。
  6. 不能作为安全依据:内容能被用户和同源脚本修改,XSS 也可能读到;不要保存真正的长期密钥,也不要根据本地角色字段决定服务端权限。
  7. 跨标签同步可监听 storage:同源其他标签修改时可收到事件,发起修改的当前页面不会收到自己的这个事件。

10. CDN 是什么?为什么能加速? ​

  1. 把内容缓存到多个边缘节点:用户从较合适的节点获取资源,减少每次都访问源站的距离和压力。
  2. 命中缓存就直接返回:没有缓存或需要重新验证时,节点才向源站获取,这叫回源。
  3. 常见资源是 JS、CSS、图片和视频:是否缓存 API 响应要看业务,用户私有数据不能误当公共资源缓存。
  4. 更新策略很重要:带内容哈希的静态资源可以长缓存,文件变了换 URL;HTML 通常要更及时地更新。
  5. 排查旧内容要分层:可能是浏览器缓存,也可能是 CDN 缓存;刷新浏览器不一定清掉 CDN 上的旧资源。

11. 强缓存和协商缓存的区别 ​

  1. 强缓存看是否仍然新鲜:缓存允许且未过期时,浏览器可以直接使用,不必向服务器验证。常见控制头是 Cache-Control: max-age。
  2. 协商缓存要发请求验证:常见是带 If-None-Match 对应 ETag,或 If-Modified-Since 对应 Last-Modified。
  3. 没变通常返回 304:服务端不再发送完整正文,浏览器使用已有缓存;内容变了通常返回 200 和新内容。
  4. no-cache 不等于不存:可以保存,但再次使用前要验证;no-store 才是要求不存储该响应。
  5. 校验器各有特点:ETag 是资源版本标识,不一定是文件哈希;Last-Modified 基于修改时间,同时存在时条件校验通常优先使用 ETag。
  6. 结合资源类型配置:带哈希的静态资源适合长缓存;HTML 可配置重新验证。用户敏感响应要避免进入公共缓存。

参考:HTTP 缓存。