type
status
date
slug
summary
tags
category
icon
password

HTTP

重定向

HTTP重定向是一种服务器响应客户端请求的方式,它告诉客户端去访问另一个URL。当服务器发送一个重定向响应时,它会返回一个特定的状态码(如301或302),并在响应头中包含Location字段,指示新的URL位置。客户端收到这个响应后,通常会自动发起对新URL的请求。
port: 4514:
  • 指定了开发服务器的端口号为 4514。当你启动开发服务器时,项目会在 http://localhost:4514https://localhost:4514 运行。
proxy: {}:
  • proxy 是一个代理选项,用于将本地请求转发到其他服务器,通常用于开发时避免跨域问题。
  • 通过这些代理配置,当客户端请求这些路径时,开发服务器会把请求转发到相应的目标服务器(即 target 所定义的服务器)。
changeOrigin: true:
  • 代理服务器会将 Host 头修改为目标服务器的地址,以确保请求正确地被目标服务器处理。
代理服务器解决跨域问题:
  • 在本地开发环境中,前端代码可能运行在 localhost(例如,http://localhost:4514)。如果这段前端代码要请求一个外部 API(例如,https://dekt.bupt.edu.cn/api),直接请求会触发浏览器的同源策略,从而被阻止。
  • 设置代理服务器:
    • 前端并不直接请求外部 API,而是先向本地开发服务器(如 localhost:4514)发送请求。
    • 代理服务器接收到前端的请求后,会将这个请求转发到实际的目标服务器(如 https://dekt.bupt.edu.cn/api)。代理服务器充当一个中间人,它和外部服务器通信,而浏览器看到的只是和本地开发服务器通信,因此没有跨域限制。
  • 当 changeOrigin 设置为 true 时,代理服务器在转发请求时会修改请求头中的 Host 字段。目标服务器(如 https://dekt.bupt.edu.cn)会看到请求的 Host 为它自己的域名,从而认为这是一个直接发给它的请求。
  • 结果:请求看起来像是直接从目标服务器自身发来的,而不是从另一个源(localhost:4514)发来的。因此,目标服务器不会触发同源策略的检查,也不需要额外处理 CORS 请求。
    • 通过这种代理,浏览器只与本地服务器交互,不触发跨域限制,同时通过修改 Host,让远程服务器正确处理请求,避免 CORS 配置复杂化。

https加密

先非对称加密得到会话密钥,再使用会话密钥进行后续的对称加密
notion image

HTTP1.1和HTTP2

  1. 多路复用:
    1. HTTP1的队头阻塞,即使使用持久连接,请求/响应需按序处理;而且浏览器对同一域名并发连接数有限
    2. HTTP2允许在单个TCP连接上并行传输多个请求和响应,通过二进制分帧层将数据分解为独立的帧(带有流ID标识),接收端根据流ID重组
  1. 头部压缩:
    1. HTTP1的头部冗余,如cookie、user-agent等字段重复发送
    2. 优化:采用了HPACK算法压缩头部,使用静态表和动态表
  1. 服务器推送:
    1. HTTP1需要客户端显示请求所有资源,增加延迟
    2. 改进后服务器可以主动推送客户端可能需要的资源,无需等待请求

HTTP3

  1. 底层协议:
    1. HTTP2依赖于TCP协议,依赖TCP的可靠传输机制,但是会影响性能
    2. HTTP3依赖于UDP协议,引入新的协议QUIC,这个QUIC在UDP之上实现了类似TCP的可靠性机制(如丢包重传),同时优化了连接建立和传输效率
  1. 优化:
    1. 使用UDP+QUIC,将连接建立和加密协商合并,减少握手次数(通常只需1-RTT或0-RTT)
    2. 在QUIC中,每个HTTP流(Stream)独立传输,丢包只会影响当前流,其他流不受影响,彻底解决了队头阻塞问题。HTTP2中,在单个TCP连接上实现多路复用(多个请求/响应并行传输),如果TCP数据包发生丢失,后续所有数据包会被阻塞(TCP队头阻塞),即使它们属于不同的HTTP流。
    3. QUIC的拥塞控制算法在用户空间实现,可快速迭代优化。UDP是无连接的,允许QUIC在应用层实现自定义可靠性机制,而无需依赖内核的TCP协议栈。
    4. 通过UDP实现更快的连接建立、更低的延迟、更好的抗丢包能力,并彻底解决队头阻塞问题。

Cookie/LocalStorage/Session/SessionStorage

cookie:
  • 生命周期
    • 会话期cookie:浏览器关闭后会被自动删除
    • 持久性cookie:取决于Expires(指定日期)和Max-Age(指定一段时间)。当cookie的过期时间被设定时,设定的时期和时间只与客户端相关,而不是服务端
    • 可以通过设置HttpOnly防止js访问,跨域cookie隔离
    • cookie的常用属性
      • 参数名
        描述
        name
        Cookie 的名称。
        value
        Cookie 的值。
        expires
        Cookie 的过期时间(GMT 格式字符串)。
        max-age
        Cookie 的生存时间(秒)。
        path
        Cookie 的路径,默认为当前路径。
        domain
        Cookie 的域名,默认为当前域名。
        secure
        仅通过 HTTPS 传输 Cookie。
        samesite
        控制 Cookie 的跨站行为,可选值:StrictLaxNone
    localStorage:
    • 数据不会自动过期,除非被手动删除;就算是关闭浏览器或者重启设备后仍然存在
    • 在无痕模式下,数据会在关闭最后一个隐私窗口时被清除
    • 不同域名下面的localStorage是隔离的,同一域名不同端口也隔离
    • 只能通过js代码访问
    Session:
       

      WebSocket

      WebSocket 是一种全双工、持久化的通信协议,专为实时双向通信设计
      1. 全双工通信:
        1. 客户端和服务器可以同时发送和接收数据,无需等待对方响应(如聊天、游戏场景)。HTTP 是“请求-响应”模式,客户端必须主动发起请求,服务器才能响应。
      1. 持久化连接:
        1. 通过一次 HTTP 握手升级为 WebSocket 协议后,连接会保持打开状态,避免重复建立连接的开销。
        2. 传统 HTTP 实现实时通信需频繁轮询(如每几秒请求一次),效率低且延迟高。
      1. 协议内容:
        1. 通过 HTTP/1.1 的 Upgrade 头字段协商升级为 WebSocket 连接(如 Upgrade: websocket
      1. 心跳机制:
        1. 通过发送 Ping 帧(服务器→客户端)和回复 Pong 帧(客户端→服务器)检测连接状态,避免因超时断开。

      WebService

      WebService是一个SOA的应用程序,不依赖于语言、平台,可以实现不同的语言间的相互调用,通过Internet进行基于HTTP协议的网络应用间的交互
      • 一种跨编程语言和跨操作系统平台的远程调用技术
      • 从表面上看,WebService就是一个应用程序,它向外界暴露出一个能够通过Web进行调用的API
      Web服务:
      • Web服务就是一个URL资源,客户端可以通过编程方式请求得到它的服务,而不需要知道所请求的服务是怎样实现的
      • 体系结构:基于Web服务提供者、Web服务请求者、Web服务中介者三个角色,和发布、发现、绑定三个动作
        • Web服务中介者:把一个Web服务请求者与合适的Web服务提供者联系在一起,它充当管理者的角色
      soap协议:
      • 一种基于xml的协议,用于通过HTTP访问web服务,一条soap消息就是一个普通的xml文档,所有构成元素均被声明于针对soap封装的默认命名空间中
      • 定义了web服务如何相互通信或如何与调用它们的客户端应用程序通信
      XFire框架:
      • 下一代的Java SOAP框架,XFire提供了非常方便的api,使用这些api可以开发面向服务的程序
      WSDL:
      • 网络服务描述语言,基于xml,用于描述WebService以及如何对它们进行访问

      WebWorker

      JS运行在浏览器的JS引擎线程上,所以JS是单线程的,WebWorker就可以为JavaScript创造多线程环境,允许主线程创建Worker线程,将一些任务分配给后者运行。主线程运行的同时,Worker线程在后台运行,两者互不干扰,等到Worker线程完成计算任务,再把结果返回给主线程。
      好处:一些计算密集型或高延迟的任务,被Worker线程负担了,主线程(UI交互)就会很流畅,不会被阻塞或者拖慢
      限制:
      • 同源:分配给worker线程运行的脚本文件必须与主线程的脚本文件同源
      • dom限制:worker线程所在的全局对象与主线程不一样,无法读取主线程所在网页的dom对象,也无法使用document、window这些对象,但是可以使用navigator对象和location对象
      • 通信联系:worker线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成
      用法:
      主线程调用Worker()构造函数新建一个Worker线程:
      • 参数为一个脚本文件,就是worker线程所要执行的任务
      • Worker不能读取本地文件,所以这个脚本必须来自网络
      主线程调用worker.postMessage()方法,向Worker发消息:
      • 参数为主线程要传给Worker的数据,可以是各种类型,包括二进制数据
      主线程通过worker.onmessage监听指定函数,接收子线程发回来的消息:
      • event的data属性可以获取Worker发来的数据
      主线程关掉Worker:
      Worker线程:
      • 监听函数的参数是一个事件对象,它的data属性包含主线程发来的数据。self.postMessage()方法用来向主线程发送消息。

      WebAssembly

      WebAssembly(简称Wasm) 是一种低级的二进制指令格式,专为在Web浏览器中高效执行而设计。它的核心目标是提升Web应用的性能,尤其是处理计算密集型任务(如游戏、音视频处理、图形渲染等)。以下是其关键特点和应用场景:

      核心特点

      1. 接近原生性能
          • 编译自C/C++/Rust等语言,运行速度比JavaScript快数倍,适合对性能要求高的场景。
          • 直接以二进制代码运行,无需解释或即时编译。
      1. 跨平台与可移植
          • 可在浏览器、Node.js、嵌入式设备等多种环境中运行。
          • 支持将现有代码库(如C/C++库)移植到Web端。
      1. 与JavaScript协同工作
          • 与JavaScript无缝集成,可通过API互相调用。
          • 适合用JavaScript处理UI逻辑,Wasm处理底层计算。
      1. 安全沙箱环境
          • 运行在浏览器沙箱中,无法直接访问DOM或系统资源,需通过JavaScript代理操作。

      典型应用场景

      1. 游戏与图形渲染
          • 将Unity/Unreal引擎的游戏编译为Wasm,实现高性能3D游戏。
          • 例如:Google Earth Web版、Figma的设计工具。
      1. 音视频处理
          • 实时视频剪辑、音频解码(如FFmpeg编译为Wasm)。
          • 例如:Zoom的Web客户端使用Wasm优化视频编码。

      代码示例

      1. C++代码编译为Wasm
          • 使用Emscripten编译:
        1. JavaScript调用Wasm函数

          与JavaScript对比
          特性
          WebAssembly
          JavaScript
          性能
          接近原生,适合计算密集型任务
          解释执行,适合逻辑与交互
          语言支持
          C/C++/Rust等编译型语言
          原生支持,动态类型语言
          适用场景
          游戏、音视频、科学计算
          UI交互、网络请求、业务逻辑
          安全性
          沙箱中运行,无直接DOM访问
          可操作DOM,需防范XSS等

          总结

          WebAssembly不是替代JavaScript,而是其性能补充。通过将关键模块用Wasm实现,可大幅提升Web应用的处理能力,尤其在需要高性能的场景下(如游戏引擎、实时数据处理)。其生态仍在快速发展,未来可能进一步扩展Web应用的能力边界。

          ES6新特性

          1. 箭头函数
            1. 模板字符串
              1. 解构赋值
                1. Promise
                  1. Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
                    所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

                BFC

                浮动元素和绝对定位元素,非块级盒子的块级容器(例如 inline-blocks, table-cells, 和 table-captions),以及overflow值不为“visiable”的块级盒子,都会为他们的内容创建新的BFC(块级格式上下文)。
                可以将 BFC 看作是页面内的一个迷你布局。一旦一个元素创建了一个 BFC,它就包含了所有的内容。正如我们所看到的,这包括浮动的元素,它们不再从盒子底部伸出来。BFC 还会导致一些其他有用的行为。
                1. 防止margin折叠/阻止元素被浮动元素覆盖
                  1. 多列布局中
                    1. 如果我们创建一个占满整个容器宽度的多列布局,在某些浏览器中最后一列有时候会掉到下一行。这可能是因为浏览器四舍五入了列宽从而所有列的总宽度会超出容器。但如果我们在多列布局中的最后一列里创建一个新的BFC,它将总是占据其他列先占位完毕后剩下的空间
                      • 形成独立渲染区域,让渲染区域内部元素的渲染不会影响外界
                      • 形成bfc条件
                        • 浮动元素:float不是none
                        • 绝对定位元素:position是absolute或者fixed
                        • 块级元素overflow不是visible
                        • flex元素
                        • inline-block元素

                  正则匹配

                  • ^:匹配字符串的开头。
                  • $:匹配字符串的结尾。
                  • .:匹配任意单个字符,除了换行符。
                  • *:匹配前面的字符零次或多次。
                  • +:匹配前面的字符一次或多次。
                  • ?:匹配前面的字符零次或一次。
                  • []:匹配方括号中的任意一个字符。
                  • [^]:匹配不在方括号中的任意一个字符。
                  • ():分组匹配,可以在后面使用 \1、\2 等来引用分组。
                  • | :匹配左右两边任意一个表达式
                  • {}:表示匹配重复次数,用于指定匹配的字符或子表达式出现的次数。例如,{3}表示匹配前面
                  • 字符或子表达式恰好出现3次,{2,5}表示匹配前面的字符或子表达式出现2到5次,{2,}表示匹配
                  • 面的字符或子表达式至少出现2次。
                  • \d:匹配任意一个数字字符,相当于[0-9]。
                  • \w:匹配任意一个字母、数字或下划线字符,相当于[A-Za-z0-9_]。
                  • \s:匹配任意一个空白字符,包括空格、制表符、换行符等。
                  • i:忽略大小写。
                  • g:全局匹配。
                  • m:多行匹配。
                  要求写出 区号+8位数字,或者区号+特殊号码: 10010/110,中间⽤短横线隔开的正则验证。 区号就 是三位数字开头。例如 010-12345678

                  ts中实现文件(pdf)预览和下载

                  a标签的target属性:
                  • _self(默认):在当前页面打开链接
                  • _blank:在新标签页打开链接
                  • _parent:在父级框架中打开链接(适用于 iframe
                  • _top:在最外层窗口中打开链接(适用于 iframe
                  a标签的本质功能是:页面跳转和支持新标签打开,可以用ts/js来实现a标签的功能:
                  • 实现pdf预览的逻辑:
                    • window.location.origin:获取网站根域名
                    • location.href:当前url
                    • pdfjs/web/viewer.html 是PDF.js 的在线查看器,file参数指向文件地址
                    • new URL(input, base) 的参数
                      • input(必填):
                        • 需要解析的 URL 字符串(可以是绝对路径或相对路径)。
                      • base(可选):
                        • 如果 input 是相对路径,base 会作为基础 URL,用于解析 input
                        • 如果 input 已经是绝对 URL,base 会被忽略。
                  但是这样打开有个缺点:不能触发a 标签的下载行为(download 属性)。
                  解决:

                  BOM/window相关

                  在浏览器中, window 对象有双重⻆⾊,即是浏览器窗⼝的⼀个接口,又是全局对象
                  location对象:
                  • href:当前窗口的完整url
                  • host:服务器名称和端口号
                  • hostname:服务器名称
                  • pathname:当前url的路径部分
                  • origin:协议、主机名和端口号部分,不包括路径、查询字符串和哈希部分
                  navigator对象:
                  • 主要⽤来获取浏览器的属性,区分浏览器类型
                  screen对象:
                  • 保存的纯粹是客户端能⼒信息,也就是浏览器窗口外面的客户端显示器的信息,比如像素宽度和像素高度
                  history对象:
                  • 主要⽤来操作浏览器 URL 的历史记录,可以通过参数向前,向后,或者向指定 URL 跳转
                    • notion image

                  Nginx

                  高性能Web服务器,C语言编写,专门设计用于高并发、低内存占用的场景
                  • Web服务器:静态资源(HTML、CSS、JS、图片)托管
                    • 比如说开发博客的时候,将博客静态的html文件移动到Nginx的html文件夹下,就可以将静态页面部署到Nginx
                  • 反向代理:隐藏后端服务器,提升安全性
                  • 负载均衡:分发请求,提高服务器性能
                  • 动静分离:静态资源由Nginx处理,动态请求交给后端
                  • 缓存加速:减少后端压力、提高访问速度
                  反向代理:
                  比如一个网站只有一个域名,但是背后有很多台服务器,不能让client知道这些服务器的ip,因此使用反向代理,让用户访问那个暴露的域名时,实际上请求被转发到后面的服务器。从而隐藏真实的服务器ip和端口号。
                  配置文件nginx.config:
                  • worker_processes: auto:根据cpu内核数量来确定Nginx工作进程数量
                  • upstream backend:添加代理服务器的配置,server ip地址:端口号
                  • server块:location配置:
                    • 例子:
                      • 表示以/app开头的请求都代理到刚刚配置的upstream中。默认用轮询的方式来代理,可以用weight来配置权重,来实现负载均衡。也可以用ip_hash,根据客户端ip地址进行hash,一个ip被代理到一个特定的服务器。

                  http缓存

                  http强制缓存:
                  服务器返回资源的时候,在响应头中设置cache-control和max-age,浏览器就会在本地缓存中存储返回的资源。浏览器再次访问这个网站的时候就会根据该缓存是否过期判断还需不需要再次向服务器请求资源。
                  加快页面加载速度,减少向服务器发起http请求的次数。
                  Cache-Control也可以设置no-cache,浏览器就不会缓存了。
                  http协商缓存:
                  是一种服务端的缓存策略,服务器会返回资源和资源标识,每次浏览器发送请求的时候还要给服务器发送资源标识,服务器就会判断浏览器的缓存版本,如果缓存版本跟最新版本一致,服务器就返回304状态码,浏览器就直接从缓存里面拿资源,否则返回200+最新资源+最新资源标识。
                  减少请求体积,加快资源读取。

                  indexedDB

                  使用场景:
                  所有的场景都基于客户端需要存储大量数据的前提下:
                  1. 数据可视化等界面,大量数据,每次请求会消耗很大性能。
                  1. 即时聊天工具,大量消息需要存在本地。(chatGPT)
                  1. 其它存储方式容量不满足时,不得已使用IndexedDB
                  IndexedDB没有表的概念,它只有仓库store的概念,它的store就相当于一张表
                  索引:我们可以在创建store的时候同时创建索引,在后续对store进行查询的时候即可通过索引来筛选,给某个字段添加索引后,在后续插入数据的过程中,索引字段便不能为空。
                  游标:cursor,比如我们要查询满足某一条件的所有数据时,就需要用到游标,我们让游标一行一行的往下走,游标走到的地方便会返回这一行数据,此时我们便可对此行数据进行判断,是否满足条件。
                  notion image

                  open的封装

                  • indexedDB.open()方法返回一个 IDBRequest 对象。这个对象通过三种事件errorsuccessupgradeneeded,处理打开数据库的操作结果。
                  • 通过事件对象的target.result属性,拿到数据库实例。
                  • 新建数据库与打开数据库是同一个操作。如果指定的数据库不存在,就会新建。不同之处在于,后续的操作主要在upgradeneeded事件的监听函数里面完成,因为这时版本从无到有,所以会触发这个事件。

                  同源/跨域

                  浏览器只有在请求的协议、域名和端口都相同的情况下,才允许发起跨域的HTTP请求,否则浏览器会阻止请求,产生跨域问题。

                  jsonp(JSON with Padding)

                  通过浏览器的<script>标签绕过浏览器的同源策略实现跨域请求。原理:<script>标签不受同源策略限制
                  1. 发起请求
                    1. 前端构造一个script标签,将请求参数(包括回调函数的名称)作为查询参数传递给后端API
                      由于script标签不受同源策略的限制,浏览器会向指定的跨域服务器发送请求,获取响应
                  1. 服务器返回数据
                    1. 服务器收到请求之后,不返回标准的JSON数据,而是将数据包装成一个函数调用的形式返回给客户端
                  1. 处理响应
                    1. 浏览器执行返回的js代码,其中会调用客户端传递的回调函数,传递数据给前端
                  示例:
                  • 请求URL中传递了回调函数名 callback = handleResponse
                  • <script> 标签被添加到 body 后,浏览器会立即加载并执行指定的 src 地址,也就是跨域的请求
                  • 后端会返回一个handleResponse()调用,并将数据作为参数传给该函数
                    问题:
                    • JSONP 是通过注入 <script> 标签来执行的,这意味着如果服务器返回的代码被恶意篡改,可能会执行任意的 JavaScript 代码,带来安全风险(如 XSS 攻击)
                    • JSONP 只能通过 <script> 标签发送 GET 请求,不能使用其他 HTTP 方法(如 POST)

                    CORS

                    原理:通过在HTTP响应头中增加特定的CORS头,允许跨域访问资源。通过允许服务器标示除了它自己以外的其他origin,这样浏览器可以访问加载这些资源。
                    1. 预检请求
                      1. 在一些跨域请求被发送之前,浏览器会自动发起一个OPTIONS请求,用于检查目标服务器是否允许该跨域请求
                        预检请求包含了请求方法、请求头等信息,服务器响应时会通过CORS头部告知浏览器是否允许该请求
                    1. 实际请求
                      1. 如果预检请求成功,浏览器会发起实际请求
                    CORS响应头部:
                    1. Access-Control-Allow-Origin
                      1. 用于指定允许哪些源(origin)访问资源。如果服务器允许某个源的跨域请求,它会在响应头中添加Access-Control-Allow-Origin
                        • Access-Control-Allow-Origin: *:允许所有域访问。
                        • Access-Control-Allow-Origin: https://example.com:只允许来自 https://example.com 的请求。
                    1. Access-Control-Allow-Methods
                      1. 指定允许的请求方法,如GET、POST、PUT
                        Access-Control-Allow-Methods: GET, POST, PUT:允许 GETPOSTPUT 请求。
                    1. Access-Control-Allow-Headers
                      1. 指定允许的自定义请求头部
                        Access-Control-Allow-Headers: Content-Type, Authorization
                    1. Access-Control-Max-Age
                      1. 指定预检请求的结果能缓存多长时间。如果服务器返回了此字段,浏览器将不会每次请求都发送预检请求,而是根据该缓存时间直接使用缓存的预检结果
                    后台:需要在HTTP响应头中设置正确的CORS相关头部,来允许特定的跨域请求
                    Cookie:前端的请求带上Cookie
                    • 需要在发送请求时设置 credentialsinclude,这样浏览器才会在跨域请求中带上 cookies
                      • 如果你的后台服务器设置了 Access-Control-Allow-Credentials: true,并允许某个特定来源的跨域请求,浏览器就会把 cookie 等凭证信息一起发送过去

                      postMessage

                      postMessage 是 HTML5 提供的 API,允许不同源的窗口/iframe之间安全地传递数据
                      • 这里拿到一个iframe,然后加载的时候就向子页面发送一个信息:
                        • action: ‘getUserData’
                      • 监听事件,看是否来自子页面,是的话就打印出来子页面返回的用户信息
                      • 这里子页面监听事件,看事件是否为getUserData,如果是就向origin发送userData

                      流式响应(text/event-stream)

                      Streaming response 的核心思想是逐步、连续地传输数据,类似于一条不断流动的河流,数据就像河水那样,一小块一小块地流向下游。在 Web 通信中,传统的 HTTP 请求-响应模式通常是一种阻塞操作,也就是说,服务器在完全准备好所有要发送的数据之后,才会把数据一次性地发送给客户端。而在 streaming response 中,服务器可以在数据逐步生成时就将其传输到客户端,而不需要等待所有数据的准备过程结束。
                      流式传输依赖于底层的 TCP 协议。在 TCP 连接中,数据可以分段发送,并且接收端会根据接收到的各个数据包逐步还原出完整的数据流。因此,流式响应实际上是对底层 TCP 机制的一种应用。
                      当一个客户端向服务器发送请求时,服务器会开始准备响应内容。如果服务器采用了流式响应机制,就可以在一部分数据准备好时立刻开始发送。这样一来,客户端不需要等待整个响应内容完全准备好才开始接收,而是可以即时地接收到数据的各个部分。为了实现这种传输,HTTP 响应头中的 Transfer-Encoding 一般被设置为 chunked,表示响应体将被分块传输。 响应头必须包含的字段:
                      当时出现的问题:主要是Connection写了close,导致响应实际上没有用到流式,而是前端发出一个请求之后,后端将所有chunk都发过来之后前端才接收到一个response
                      服务器返回 Connection: close 时:
                      • 连接立即终止:服务器在发送完当前响应后会 立即关闭 TCP 连接。
                      • 流式数据中断:客户端只能收到服务器在关闭连接前已经发送的数据,后续数据被截断。
                      • 实时性失效:所有数据会在连接关闭后一次性到达客户端,而非逐块实时触发。
                      导致这里面的onMessage没有被触发:
                       
                      gns3的配置JS/TS
                      • Giscus