网络与浏览器
1. WebSocket 和 SSE 的区别
- 通信方向不同:WebSocket 支持双方随时发消息;SSE 主要是服务器持续向浏览器推送,浏览器提交数据通常另发 HTTP 请求。
- 协议不同:WebSocket 使用
ws://或加密的wss://;SSE 基于 HTTP,响应类型为text/event-stream。 - 数据不同:WebSocket 支持文本和二进制,JSON 要自行序列化;SSE 传 UTF-8 文本,也能在文本里放 JSON。
- 重连不同:浏览器原生
EventSource支持自动重连;原生 WebSocket 通常要自己实现重连、退避和心跳。连接都可能被断开,不是“一次握手永久通信”。 - 场景不同:聊天、游戏、协同编辑适合 WebSocket;通知、日志、AI 文本流等服务端推送场景可以考虑 SSE。
- 注意客户端限制:原生
EventSource主要使用 GET,不能随意设置请求头;需要 POST 或自定义鉴权头时,可以用fetch读取流,但事件解析和重连要另外实现。
参考:WebSocket、EventSource。
2. WebSocket 怎么连接?有哪些重要属性?
- 创建连接:使用
new WebSocket(url),随后等待open事件,不能创建后立即假设连接成功。 - 收发消息:连接打开后用
send()发送,在message事件中读取event.data。收到 JSON 文本时要处理解析失败。 - 处理异常:监听
error和close;重连使用逐渐延长的等待时间,避免故障时大量客户端一起重试。 - 重要属性:
readyState表示连接状态;bufferedAmount是排队等待发送的字节数;binaryType控制接收二进制数据的形式。 - 四种状态:
CONNECTING是连接中,OPEN是可通信,CLOSING是关闭中,CLOSED是已关闭。 - 业务可靠性自己设计:断线期间可能丢消息,重要消息需要编号、确认、补拉和去重。浏览器原生 API 不提供自动流量背压,消息过多时要限流或合并。
- 离开页面要清理:关闭不再需要的连接、心跳和重连定时器。服务端还要验证用户身份和请求来源,不能只依赖 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 是什么?为什么需要它们?
- MCP 连接工具和数据:让 AI 应用按统一约定使用搜索、文件、业务接口等能力,减少每接一个系统就重写一套对接代码。
- A2A 连接不同 Agent:让不同系统的 Agent 发现彼此能力、交接任务和交换结果,不需要暴露对方全部内部实现。
- AG-UI 连接 Agent 和用户界面:约定文本流、工具状态、应用状态等事件,让前端知道任务进行到了哪一步。
- 三者解决不同层面的问题:可以理解成“调用工具、Agent 协作、与界面交互”,并不互相替代,也不要求项目同时使用。
- 协议不等于权限和业务实现:接入后仍然需要鉴权、参数校验、错误处理和权限控制,也不会自动让 AI 的结果正确。
4. 如何在页面上渲染数学公式?
- 先确定输入格式:常见是 LaTeX 字符串,例如
x^2 + y^2 = z^2,不能直接当普通 HTML 显示成公式。 - 使用专门的渲染库:常见有 KaTeX 和 MathJax,根据公式支持范围、性能和无障碍要求选择。
- 和 Markdown 配合:配置数学公式插件,约定行内公式和块级公式的分隔符;只写
$...$不代表浏览器会自动渲染。 - 流式内容要等边界完整:收到半个公式时先保留文本,等公式结束再渲染,避免频繁报错和抖动。
- 处理异常和安全:非法公式显示原文或错误提示;限制输入长度,谨慎开放可加载外部资源或输出 HTML 的扩展功能。
参考:KaTeX 配置选项。
5. OPTIONS 是什么?什么时候触发预检?
- OPTIONS 是 HTTP 方法:用于询问服务端支持哪些通信选项。CORS 预检会用它,但不是所有 OPTIONS 请求都是预检。
- 不是所有跨域请求都有预检:满足 CORS 安全列表条件的请求可以直接发送;跨域使用 PUT、DELETE、自定义请求头或
application/json等通常会触发预检。 - 浏览器先询问服务器:预检包含
Origin、Access-Control-Request-Method,必要时包含Access-Control-Request-Headers。 - 服务器返回允许范围:使用
Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers等响应头。 - 不要记错字段:请求头是单数的
Access-Control-Request-Method,不是Methods;没有标准的Access-Control-Request-Credentials预检请求头。 - 预检成功才继续实际请求:实际响应仍要满足 CORS 条件。预检许可可以用
Access-Control-Max-Age缓存,但受浏览器限制。
参考:CORS 预检。
6. CORS 是什么?解决什么问题?
- CORS 是跨源资源共享:服务器用响应头告诉浏览器,哪些来源的脚本可以读取自己的响应。
- 核心是服务端允许:例如允许指定前端域名。前端自己加一个
Access-Control-Allow-Origin请求头不能解决问题。 - 携带 Cookie 要配合配置:跨源
fetch通常需要credentials: 'include',服务端允许凭证,并返回具体允许的 Origin,不能用*。 - Cookie 规则还会独立生效:
SameSite、Secure、第三方 Cookie 限制等,可能导致 Cookie 仍然不发送。 - CORS 不是鉴权:不阻止非浏览器客户端请求,也不能单独防止 CSRF。有的请求已经到达服务器,只是浏览器不让脚本读取响应。
参考:MDN CORS。
7. 怎么理解跨域?怎么解决?
- 比较协议、主机和端口:三者有一个不同就是不同源。
a.example.com和b.example.com也不同源;路径不同不算跨域。 - 浏览器限制部分跨源访问:主要保护数据读取和页面互相访问,不是说所有跨源资源都不能加载。
- 常见方案是 CORS 或反向代理:后端配置 CORS;或者由同源服务转发 API 请求,浏览器只访问同源入口。
- 开发代理只解决开发环境:Vite 的开发代理不会自动变成线上代理,生产环境需要单独配置网关或服务端。
no-cors不是万能解决办法:响应通常变成不透明响应,JavaScript 读不到正文;关闭浏览器安全设置也不是正式方案。
8. Cookie 和 Session 的区别
- Cookie 是浏览器保存的小段数据:符合域名、路径和安全规则时,会随 HTTP 请求自动发送。
- Session 是服务端会话机制:服务端保存用户会话数据,浏览器通常通过 Cookie 携带一个 session ID,让服务端找到对应会话。
- 两者常常配合使用:Cookie 可以存会话标识,也能存其他小数据;Session 不等于 Cookie 本身。
- 过期需要两边考虑:Cookie 有过期设置,服务端 Session 也有有效期;删除 Cookie 不一定立即删除服务端记录。
- 安全属性要理解:
HttpOnly阻止脚本直接读取 Cookie,Secure限制通过 HTTPS 发送,SameSite控制部分跨站携带行为。它们不能替代服务端鉴权。 - 多台服务要共享会话或统一校验:否则请求换一台机器,就可能找不到登录信息。
9. LocalStorage 存状态需要考虑什么?什么时候取不到?
- 只存字符串:对象需要序列化,读取时处理 JSON 格式错误,并校验字段结构。
- 按 Origin 隔离:协议、域名或端口变了,就不是同一个存储空间;同源不同路径通常共享。
- 没有自动过期机制:业务需要保存过期时间,并在读取时判断;数据版本变化时也要迁移或清理。
- 它是同步 API:频繁写入大对象会阻塞主线程,不适合大量数据和高频更新。
- 可能取不到或直接抛错:用户清理数据、键不存在、隐私模式、存储被禁用或容量不足都会影响使用。SSR 中还可能根本没有
window。 - 不能作为安全依据:内容能被用户和同源脚本修改,XSS 也可能读到;不要保存真正的长期密钥,也不要根据本地角色字段决定服务端权限。
- 跨标签同步可监听
storage:同源其他标签修改时可收到事件,发起修改的当前页面不会收到自己的这个事件。
10. CDN 是什么?为什么能加速?
- 把内容缓存到多个边缘节点:用户从较合适的节点获取资源,减少每次都访问源站的距离和压力。
- 命中缓存就直接返回:没有缓存或需要重新验证时,节点才向源站获取,这叫回源。
- 常见资源是 JS、CSS、图片和视频:是否缓存 API 响应要看业务,用户私有数据不能误当公共资源缓存。
- 更新策略很重要:带内容哈希的静态资源可以长缓存,文件变了换 URL;HTML 通常要更及时地更新。
- 排查旧内容要分层:可能是浏览器缓存,也可能是 CDN 缓存;刷新浏览器不一定清掉 CDN 上的旧资源。
11. 强缓存和协商缓存的区别
- 强缓存看是否仍然新鲜:缓存允许且未过期时,浏览器可以直接使用,不必向服务器验证。常见控制头是
Cache-Control: max-age。 - 协商缓存要发请求验证:常见是带
If-None-Match对应 ETag,或If-Modified-Since对应 Last-Modified。 - 没变通常返回 304:服务端不再发送完整正文,浏览器使用已有缓存;内容变了通常返回 200 和新内容。
no-cache不等于不存:可以保存,但再次使用前要验证;no-store才是要求不存储该响应。- 校验器各有特点:ETag 是资源版本标识,不一定是文件哈希;Last-Modified 基于修改时间,同时存在时条件校验通常优先使用 ETag。
- 结合资源类型配置:带哈希的静态资源适合长缓存;HTML 可配置重新验证。用户敏感响应要避免进入公共缓存。
参考:HTTP 缓存。