前端核心知识点学习笔记

7 阅读34分钟

前端核心知识点学习笔记

一、浏览器渲染机制

核心逻辑:浏览器从获取URL到最终将页面完整展示在屏幕上,是一个复杂且有序的流水线式过程,每个环节紧密关联、相互影响。理解浏览器渲染的完整流程,不仅能帮助我们快速定位前端页面的渲染问题,更是进行前端性能优化的核心前提——只有明确每个环节的作用和潜在瓶颈,才能针对性地提出优化方案,提升页面加载速度和用户体验。在实际开发中,页面卡顿、加载缓慢等问题,大多与渲染流程中的某个环节处理不当有关,因此深入掌握这部分知识,是前端开发者必备的基础能力。

1. 核心渲染流程(详细拆解)

浏览器的渲染流程并非一次性完成,而是分步骤、分阶段进行,每一步都有其明确的任务和意义,具体可拆解为七个核心步骤,从网络请求到最终渲染,形成完整的闭环。

  • 第一步:发起请求与解析HTML。当用户在浏览器地址栏输入URL并按下回车后,浏览器会首先发起HTTP请求(若URL为HTTPS,则先进行SSL/TLS握手建立安全连接),向服务器请求HTML文件。由于HTML文件可能存在较大体积,为了提升效率,浏览器采用“流式解析”模式,即边下载HTML文件边进行解析,而非等待整个文件下载完成后再解析。HTML解析器会逐行读取标签,将其转化为DOM Tree(文档对象模型树),DOM Tree是一个树形结构,每个节点对应HTML中的一个标签、文本或属性,它完整描述了HTML文档的结构和内容,是后续渲染的基础。例如,一个简单的
    Hello World
    ,会在DOM Tree中形成一个div节点,包含class属性和文本子节点。
  • 第二步:解析CSS并生成CSSOM Tree。在解析HTML的过程中,若遇到、等CSS相关标签,浏览器会暂停当前HTML解析(或并行发起CSS请求,具体取决于浏览器优化策略),下载CSS文件后由CSS解析器进行解析。CSS解析器会将CSS规则转化为CSSOM Tree(CSS对象模型树),CSSOM Tree同样是树形结构,描述了每个元素对应的样式规则,包括字体、颜色、边距、布局等信息。需要注意的是,CSS解析是阻塞性的,即CSSOM Tree未构建完成前,浏览器无法进行后续的渲染树生成步骤,这也是为什么CSS文件的加载速度会影响页面整体渲染速度。
  • 第三步:JavaScript的阻塞影响(重点解析)。解析HTML时,若遇到
  • 第四步:生成Render Tree(渲染树)。当DOM Tree和CSSOM Tree均构建完成后,浏览器会将两者进行合并,生成Render Tree(渲染树)。Render Tree的核心作用是筛选出需要参与页面渲染的元素,并将其样式与结构结合起来。需要重点注意的是,display:none的节点不会被纳入Render Tree,因为这类节点不会在页面上显示,无需参与后续的布局和绘制;而visibility:hidden的节点会被纳入Render Tree,只是不会显示,因为其仍占据页面空间,会影响布局。例如,一个设置了display:none的弹窗,在渲染时不会出现在Render Tree中,而设置了visibility:hidden的弹窗,会出现在Render Tree中,但不会被绘制出来。
  • 第五步:Layout(回流/重排)。Render Tree生成后,浏览器进入Layout阶段,也称为回流或重排。这一阶段的核心任务是计算每个元素在页面文档流中的几何位置和尺寸,包括元素的宽度、高度、边距、定位方式等,最终生成布局树(Layout Tree)。Layout过程的性能代价较高,因为一旦元素的几何属性发生变化,浏览器需要重新计算整个布局树,甚至会引发连锁反应——例如,修改一个父元素的宽度,可能会导致其所有子元素的位置和尺寸都发生变化,进而触发多次回流。
  • 第六步:Paint(绘制)。Layout阶段完成后,浏览器进入Paint阶段,即根据Render Tree和Layout Tree的信息,将每个元素的视觉样式绘制到屏幕上。绘制的内容包括元素的颜色、背景、阴影、边框、文本等所有视觉属性,浏览器会按照一定的顺序(通常是从上层到下层、从左到右)进行绘制,确保元素的视觉呈现符合预期。需要注意的是,绘制过程是按图层进行的,不同元素可能处于不同的绘制图层,这为后续的合成步骤打下基础。
  • 第七步:Composite(合成)。绘制完成后,浏览器进入Composite(图层合成)阶段。为了提升渲染效率和避免页面卡顿,浏览器会将页面拆分为多个独立的合成层,例如,设置了transform、opacity、position:fixed属性的元素,以及正在执行动画的元素,都会被单独划分成一个合成层。这些合成层会被交给GPU(图形处理器)进行处理,GPU具有并行计算能力,能够快速合并多个图层,最终将合并后的页面显示在屏幕上。采用图层合成的优势在于,当某个合成层的内容发生变化时,只需重新绘制该图层并进行合成,无需影响整个页面,从而减少渲染开销。

流程总结:HTML解析 → DOM Tree构建 → CSS解析 → CSSOM Tree构建 → Render Tree生成 → Layout(回流) → Paint(绘制) → Composite(合成) → 页面展示。这一流程是浏览器渲染的核心逻辑,每个环节都不可或缺,任何一个环节出现延迟或错误,都会影响页面的加载速度和展示效果。

2. 各环节优化技巧(补充细节与案例)

(1)HTML优化

HTML作为页面的结构基础,其编写质量直接影响DOM构建速度和后续渲染效率,因此优化HTML的核心是精简结构、减少冗余、提升解析速度。

  • 使用语义化标签。语义化标签是指具有明确含义的HTML标签,如(页面头部)、(导航栏)、(页面主体)、(内容区块)、(页面底部)等。使用语义化标签的优势有两点:一是有利于SEO(搜索引擎优化),搜索引擎爬虫能够通过语义化标签快速识别页面结构和核心内容,提升页面的搜索排名;二是便于代码维护,语义化标签能够清晰地体现页面结构,避免通篇使用
    标签导致的结构混乱,后续开发者能够快速理解代码逻辑。例如,一个导航栏使用标签,比使用
  • 合理使用id和class。id用于标识唯一元素,一个页面中id值不能重复,通常用于JS获取特定节点或锚点跳转;class用于标识多个具有相同样式或行为的元素,便于样式复用和批量操作。开发中应避免重复定义id或class,避免使用过于复杂的选择器,减少浏览器解析CSS时的匹配开销。例如,不要为多个元素设置相同的id,也不要定义不必要的class,避免样式冗余。
  • 实现懒加载。对于非首屏的DOM元素和资源(如图片、视频、iframe等),应采用懒加载机制,即页面初始加载时不加载这些内容,当用户滚动到对应区域时再进行加载。这样可以降低页面初始渲染的压力,减少HTTP请求数量,提升首屏加载速度。例如,图片懒加载可以通过设置img标签的loading="lazy"属性实现,也可以通过JS监听滚动事件,动态修改img标签的src属性。
  • 避免频繁操作DOM。DOM操作是前端性能的一大瓶颈,因为每次操作DOM都会触发回流或重绘,频繁操作会导致页面卡顿。优化方案是:先缓存DOM节点,避免每次操作都通过document.querySelector()等方法获取节点;对于批量更新DOM的场景,可使用document.createDocumentFragment()创建文档碎片,将所有需要添加的节点先添加到文档碎片中,再一次性将文档碎片添加到DOM中,这样只需触发一次回流,减少渲染开销。例如,批量添加10个li标签,使用文档碎片比逐个添加li标签效率高得多。
  • 精简HTML结构。避免嵌套过深的标签结构,嵌套层数过多会增加DOM Tree的复杂度,延长解析时间。例如,不要出现
    ...
    这样的深层嵌套,建议嵌套层数不超过6层。同时,删除页面中不必要的注释、空标签,精简HTML代码体积,提升下载和解析速度。

(2)CSS优化

CSS的解析速度和样式规则的合理性,直接影响CSSOM Tree的构建效率和后续的回流、重绘频率,因此CSS优化的核心是减少解析开销、避免不必要的样式渲染。

  • 避免使用*通配符选择器。通配符选择器会匹配页面中的所有元素,增加浏览器的选择器匹配开销,尤其是在页面元素较多的情况下,会显著降低CSS解析速度。建议替换为具体的标签选择器或class选择器,例如,将 { margin: 0; padding: 0; }替换为body, div, p, ul, li { margin: 0; padding: 0; },精准匹配需要重置样式的元素。
  • 优化图片资源处理。对于小图片(如图标、图标),将其转换为base64格式嵌入CSS中,这样可以减少HTTP请求数量,因为base64格式的图片会被直接解析,无需额外发起网络请求。但需要注意,大图片不适合转换为base64格式,否则会导致CSS文件体积过大,反而增加下载时间。对于大图片,建议使用外链形式,并设置合适的尺寸,避免图片缩放导致的重绘。
  • 抽离通用样式,实现样式复用。开发中应将页面中重复使用的样式(如按钮样式、卡片样式、文本样式等)抽离为通用样式,采用面向对象的CSS思路,通过class复用这些样式,减少CSS代码冗余。例如,定义一个.common-btn类,包含按钮的通用样式,所有按钮都可以复用该类,无需重复编写相同的样式代码。
  • 合理使用CSS变量。CSS变量(--变量名: 值;)可以统一管理页面的主题样式,如颜色、边距、字体大小等,后续修改主题时,只需修改CSS变量的值,无需逐个修改样式规则,提升维护效率。例如,定义--primary-color: #1890ff;,页面中所有需要使用主色调的元素,都可以通过var(--primary-color)引用,修改主色调时只需修改该变量即可。
  • 避免使用!important。!important会强制提升样式的优先级,若大量使用,会导致样式优先级混乱,增加调试难度,后续修改样式时需要使用更高优先级的选择器或再次使用!important,形成恶性循环。建议通过合理的选择器优先级(如id选择器优先级高于class选择器,后代选择器优先级根据嵌套层数确定)控制样式,避免使用!important。
  • 推荐使用Tailwind CSS原子类开发。Tailwind CSS是一款流行的原子类CSS框架,其核心优势在于无需手写大量CSS代码,通过预定义的原子类(如text-center、p-4、bg-blue-500等)快速构建页面样式。原子类语义清晰,能够减少命名成本(无需为每个元素定义独特的class名称);同时,Tailwind CSS支持按需编译,能够只打包页面中使用到的原子类,减少CSS文件体积;此外,原子类还能实现团队开发风格的统一,降低沟通成本,尤其适合快速开发和响应式布局开发。例如,实现一个居中对齐、内边距为16px、背景色为蓝色的按钮,只需添加class="text-center p-4 bg-blue-500"即可,无需编写自定义CSS。
  • 避免CSS阻塞渲染。将CSS文件放在标签内,这样浏览器可以在解析HTML的同时并行下载和解析CSS,避免CSS解析延迟导致的渲染阻塞。同时,对于非关键CSS(如打印样式、移动端适配样式),可以采用异步加载的方式,避免阻塞首屏渲染。

(3)Script优化

JavaScript是影响页面渲染的关键因素,其执行顺序和加载方式,直接决定了DOM构建和页面渲染的速度,因此Script优化的核心是减少阻塞、提升执行效率。

  • 合理放置script标签位置。默认情况下,script标签会阻塞DOM解析,因此建议将外联script标签放在页面底部(标签之前),这样可以避免阻塞HTML解析和首屏渲染,让页面先完成DOM构建和初步渲染,再执行JS代码。对于内联JS代码,若无需操作DOM,也可放在标签内,但需注意避免阻塞DOM构建。
  • 使用defer和async属性。为外联script标签添加defer或async属性,可避免JS阻塞DOM构建。两者的区别在于:defer属性会让JS在DOM下载完成后、DOMContentLoaded事件触发前执行,且多个带有defer属性的JS会按顺序执行;async属性会让JS在下载完成后立即执行,不保证执行顺序,多个带有async属性的JS会根据下载速度先后执行。开发中,若JS代码之间存在依赖关系(如A.js依赖B.js),应使用defer属性,确保执行顺序;若JS代码之间无依赖关系,可使用async属性,提升加载效率。例如,引入jQuery和依赖jQuery的插件时,应给两个script标签都添加defer属性,确保jQuery先执行。
  • 减少全局变量污染。使用let/const声明变量,替代var声明,因为var声明的变量会挂载到window对象上,容易造成全局变量污染,且存在变量提升、作用域不明确等问题。let/const声明的变量具有块级作用域,不会挂载到window对象上,能够有效减少全局变量冲突,提升代码可读性和安全性。例如,避免使用var name = "zhangsan";,改为const name = "zhangsan";,若变量需要修改,则使用let。
  • 优化JS执行效率。频繁操作DOM会导致多次回流和重绘,因此应尽量减少DOM操作次数,可通过缓存DOM节点、批量更新DOM等方式优化。例如,获取一个频繁操作的DOM节点时,可先缓存到变量中:const box = document.querySelector(".box");,后续操作box即可,无需每次都调用querySelector()。对于批量修改DOM的场景,可使用文档碎片或先隐藏元素,修改完成后再显示,减少回流次数。
  • 拆分复用函数,避免冗长代码。将复杂的JS逻辑拆分为多个功能单一的函数,实现函数复用,不仅便于代码维护和调试,还能提升代码可读性。例如,将表单验证、数据请求、DOM操作等逻辑分别拆分为独立的函数,调用时只需执行对应函数即可,避免一个函数包含上千行代码。
  • 优化异步逻辑。异步逻辑(如接口请求、定时器、文件读取等)应使用async/await语法,替代传统的回调函数和Promise链式调用,解决“回调地狱”问题,使代码更简洁易懂、便于调试。例如,发起接口请求时,使用async function getDate() { const res = await fetch("/api/data"); const data = await res.json(); return data; },比使用fetch("/api/data").then(res => res.json()).then(data => { ... })更简洁。同时,合理使用Promise.all()、Promise.race()等方法,优化多个异步任务的执行效率,例如,多个无依赖的接口请求,可使用Promise.all()并行发起,提升请求速度。
  • 减少JS文件体积。通过代码压缩(如使用Terser压缩JS代码)、tree-shaking(移除未使用的代码)等方式,减少JS文件体积,提升下载速度。例如,使用Webpack等构建工具,开启tree-shaking功能,移除项目中未使用的函数、变量等代码;同时,压缩JS代码,移除注释、空格、换行等冗余内容,进一步减小文件体积。

(4)性能优化核心:减少回流与重绘

回流(Layout)和重绘(Paint)是前端性能优化的重点和难点,因为两者都会消耗大量的浏览器资源,频繁的回流和重绘会导致页面卡顿、加载缓慢,影响用户体验。因此,优化的核心是减少回流和重绘的次数,降低其执行代价。

  • 核心认知:回流和重绘的区别的是,回流一定会触发重绘,而重绘不一定触发回流。回流是重新计算元素的几何位置和尺寸,涉及整个布局树的调整,性能代价极高;重绘只是重新绘制元素的视觉样式,不涉及几何位置的变化,性能代价相对较低。例如,修改元素的颜色(color),只会触发重绘;修改元素的宽度(width),会触发回流,进而触发重绘。

  • 常见触发回流的操作:修改元素的width、height、margin、padding等几何属性;修改元素的fontSize、fontFamily等字体属性;插入、删除、移动DOM节点;修改元素的position属性(如从static改为absolute);读取元素的布局属性(如el.offsetHeight、el.getBoundingClientRect()、el.scrollTop等),因为浏览器需要立即计算这些属性的值,会强制触发回流。

  • 常见触发重绘的操作:修改元素的color、background、border-color、box-shadow等视觉属性;修改元素的visibility属性(visibility:hidden不会触发回流,但会触发重绘);修改元素的background-image等背景相关属性。

  • 优化技巧:

    • 批量修改样式。避免逐个修改元素的样式属性,可通过修改class名称,一次性应用多个样式,减少回流和重绘次数。例如,要修改一个元素的宽度、高度和背景色,不要分别设置el.style.width、el.style.height、el.style.backgroundColor,而是定义一个包含这些样式的class,通过el.classList.add()添加该class。
    • 避免读取布局属性后立即修改样式。若需要读取元素的布局属性(如offsetHeight),应集中读取,避免读取后立即修改样式,导致浏览器触发多次回流。例如,不要写成:const height = el.offsetHeight; el.style.height = height + 10 + "px"; 这样会触发两次回流(一次读取,一次修改),应改为集中读取所有需要的布局属性,再统一修改样式。
    • 使用transform和opacity实现动画。transform和opacity属性的变化不会触发回流和重绘,只会触发合成层的重新合成,因为浏览器会将设置了这两个属性的元素单独作为合成层,修改时只需重新合成图层,无需影响整个页面的布局和绘制。例如,实现元素的平移、缩放、淡入淡出动画,使用transform: translate()、transform: scale()、opacity: 0.5等,比使用left、top、width等属性实现动画更高效。
    • 隐藏元素后再修改样式。对于需要批量修改样式的元素,可先设置el.style.display = "none",修改完成后再设置el.style.display = "block",这样只需触发两次回流(隐藏和显示时),避免多次修改样式导致的多次回流。
    • 使用虚拟DOM。虚拟DOM是前端框架(如React、Vue)中常用的优化手段,其核心是通过JavaScript对象模拟DOM结构,修改数据时先更新虚拟DOM,再通过diff算法对比虚拟DOM的变化,只更新真实DOM中变化的部分,从而减少回流和重绘的次数。例如,React中的Virtual DOM,通过diff算法找到最小更新范围,只更新需要变化的DOM节点,提升渲染效率。

二、GET 和 POST 的区别及HTTP请求结构

HTTP协议是前端与后端进行数据交互的核心协议,GET和POST是HTTP协议中最常用的两种请求方法,掌握两者的区别、适用场景以及HTTP请求的结构,是实现前后端数据交互的基础。在实际开发中,正确选择请求方法,能够规范接口设计、提升数据交互的安全性和效率。

1. GET与POST的核心区别(详细解析)

GET和POST的区别不仅体现在表面的数据传输方式上,更涉及语义、安全性、幂等性等多个维度,理解这些区别,能够帮助我们在实际开发中正确选择请求方法,避免因选择不当导致的接口问题。

  • 语义区别(Restful HTTP规范):GET请求的语义是“获取资源”,即从服务器获取指定的资源,请求参数用于筛选资源,不会对服务器端的数据产生任何影响;POST请求的语义是“提交数据、新增资源”,即向服务器提交数据,用于创建新的资源或执行具有副作用的操作,会改变服务器端的数据状态。例如,获取用户列表使用GET请求(/api/users),新增用户使用POST请求(/api/users),这是Restful接口设计的规范。

  • 数据传输方式(核心区别):

    • GET请求:请求参数通常放在URL的QueryString(查询字符串)中,格式为“URL?参数名1=值1&参数名2=值2”,例如/api/user?id=123&name=zhangsan。需要注意的是,GET请求的参数长度有限制,这并非HTTP协议的规定,而是由浏览器和服务器的实现决定的,通常限制在2kb-8kb之间,超过限制的参数会被截断,导致请求失败。因此,GET请求不适合传输大量数据。
    • POST请求:请求数据通常放在Request Body(请求体)中,数据格式可以是form-data(表单数据,适用于文件上传)、x-www-form-urlencoded(普通表单数据)、application/json(JSON格式数据,最常用)等。HTTP协议本身没有限制POST请求体的大小,限制通常由服务器端(如Nginx、Tomcat)配置决定,因此POST请求适合传输大量数据,如表单提交、文件上传等场景。
    • 补充说明:很多人认为GET请求不能发送请求体,这是一个误区。HTTP协议并没有禁止GET请求发送请求体,只是浏览器和服务器通常约定不使用这种方式,因为GET请求的语义是获取资源,发送请求体没有实际意义,且部分服务器会忽略GET请求的请求体,因此实际开发中不会使用GET请求发送请求体。
  • 安全性区别:GET和POST请求的数据均为明文传输(未使用HTTPS的情况下),因此都不具备绝对的安全性。相对而言,POST请求更安全,因为其数据放在请求体中,不会显示在URL中,不会被浏览器缓存、不会被历史记录保存,也不会被第三方轻易获取;而GET请求的参数显示在URL中,会被浏览器缓存、保存在历史记录中,若参数包含敏感信息(如密码、token),会存在安全隐患。需要注意的是,真正的安全性依赖于HTTPS加密传输,无论是GET还是POST,使用HTTPS后,数据都会被加密,能够有效防止数据被窃取。

  • 幂等性区别:幂等性是HTTP协议的重要概念,指多次执行相同的请求,得到的结果一致,且不会对服务器端的数据产生额外影响。GET请求具有幂等性,因为其语义是获取资源,多次请求相同的URL和参数,得到的资源是一致的,不会改变服务器端的数据;POST请求不具有幂等性,因为其语义是提交数据,多次请求相同的POST请求,会导致服务器端创建多个相同的资源,产生副作用。例如,多次发送POST请求新增用户,会创建多个相同的用户;而多次发送GET请求获取用户信息,得到的结果始终一致。

  • 缓存区别:GET请求会被浏览器自动缓存,浏览器会将GET请求的URL和响应结果缓存起来,下次发起相同的GET请求时,若缓存未过期,会直接从缓存中获取响应结果,无需向服务器发起请求,从而提升请求速度;POST请求一般不会被浏览器缓存,因为其每次请求都可能提交新的数据,产生不同的响应结果,缓存没有意义。若需要缓存POST请求的结果,需手动设置缓存相关的请求头(如Cache-Control)。

  • 请求头区别:GET请求的请求头相对简单,不需要设置Content-Type(浏览器会自动设置);POST请求需要设置Content-Type请求头,指定请求体的数据格式,例如,提交JSON数据时,Content-Type设为application/json;提交表单数据时,Content-Type设为application/x-www-form-urlencoded;上传文件时,Content-Type设为multipart/form-data。

  • 浏览器后退/刷新行为区别:GET请求后退、刷新时,浏览器会重新发起请求,但不会有提示;POST请求后退、刷新时,浏览器会提示“是否重新提交表单”,因为重新提交POST请求会再次执行提交操作,可能产生副作用(如重复提交数据)。

2. 实际开发中的选择场景

  • 使用GET请求的场景:获取资源、查询数据,且请求参数较少、无敏感信息。例如,获取用户列表、查询商品详情、搜索数据等。
  • 使用POST请求的场景:提交数据、新增资源、修改资源、删除资源(部分Restful接口中删除资源使用DELETE请求,但也可使用POST),且请求参数较多、包含敏感信息,或需要上传文件。例如,用户注册、登录(提交账号密码)、新增商品、上传图片、提交表单等。

3. 一次HTTP请求包含的信息(完整结构)

无论使用GET还是POST请求,一次完整的HTTP请求都由三部分组成:请求行、请求头、请求体(可选),三部分各司其职,共同完成数据的传输。

  • 请求行(Request Line):请求行是HTTP请求的第一行,包含三个核心信息:请求方法、请求路径、HTTP版本。

    • 请求方法:除了GET和POST,HTTP协议还有其他请求方法,各有其语义和适用场景,例如:PUT(修改资源,具有幂等性)、PATCH(部分修改资源,不具有幂等性)、DELETE(删除资源,具有幂等性)、OPTIONS(预检请求,用于跨域请求中,检测服务器允许的请求方法和请求头)、HEAD(获取资源的响应头,不返回响应体,用于快速获取资源的元信息)。
    • 请求路径:即服务器端的接口路径,例如/api/user、/api/goods/list,用于指定请求的资源位置。
    • HTTP版本:常用的HTTP版本有HTTP/1.1和HTTP/2,HTTP/1.1支持长连接、管道化请求,HTTP/2支持多路复用、头部压缩,性能更优,目前大部分浏览器和服务器都支持HTTP/2。
    • 示例:GET /api/user?id=123 HTTP/1.1(GET请求的请求行)、POST /api/user HTTP/1.1(POST请求的请求行)。
  • 请求头(Request Headers):请求头是HTTP请求的附加信息,用于告诉服务器请求的相关参数,如请求来源、数据格式、身份验证信息等,服务器根据请求头信息处理请求。常见的请求头如下:

    • Authorization:身份验证令牌,用于验证用户身份,常见格式为Bearer ,例如Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...,服务器通过解析token,确认用户的登录状态和权限。
    • Cookie:客户端存储的会话信息,由服务器端设置,客户端每次发起请求时,都会将Cookie携带在请求头中,用于维持会话状态(如登录状态)。例如,用户登录后,服务器设置Cookie,后续请求都会携带该Cookie,服务器通过Cookie识别用户身份。
    • Content-Type:指定请求体的数据格式,不同的数据格式对应不同的Content-Type值,例如:application/json(JSON格式数据)、application/x-www-form-urlencoded(普通表单数据)、multipart/form-data(文件上传数据)、text/plain(纯文本数据)。
    • User-Agent:浏览器或客户端的标识信息,包含浏览器名称、版本、操作系统等信息,服务器可根据User-Agent判断客户端类型,实现适配性处理(如区分PC端和移动端)。
    • Accept:指定客户端可接受的响应数据格式,例如Accept: application/json,表示客户端希望服务器返回JSON格式的数据。
    • Origin:跨域请求中,用于表示请求的来源(协议+域名+端口),服务器通过Origin判断是否允许跨域请求。
    • Cache-Control:用于控制请求的缓存策略,例如Cache-Control: no-cache,表示不使用缓存,每次都向服务器发起请求;Cache-Control: max-age=3600,表示缓存有效期为3600秒。
  • 请求体(Request Body):请求体是可选的,仅存在于POST、PUT、PATCH等请求中,用于携带请求数据,GET请求通常没有请求体。请求体的数据格式由Content-Type请求头指定,常见的格式有三种:

    • application/json:最常用的数据格式,JSON格式简洁、易解析,适合传输复杂数据(如对象、数组),例如{"name": "zhangsan", "age": 20}。
    • application/x-www-form-urlencoded:普通表单数据格式,数据以“参数名1=值1&参数名2=值2”的形式传输,适合简单的表单提交(如登录表单)。
    • multipart/form-data:用于文件上传,可同时传输文件和普通表单数据,例如上传图片时,请求体中包含文件数据和其他表单参数。

4. 常见HTTP响应状态码(补充延伸)

在前后端数据交互中,了解HTTP响应状态码,能够快速定位请求问题,例如请求成功、请求错误、服务器错误等。常见的状态码分为五大类,核心状态码如下:

  • 1xx(信息性状态码):表示服务器已接收请求,正在处理,例如100 Continue(服务器已接收请求头,等待请求体)。
  • 2xx(成功状态码):表示请求成功,例如200 OK(请求成功,返回正常响应数据)、201 Created(新增资源成功,如POST请求新增用户成功)。
  • 3xx(重定向状态码):表示请求需要进一步操作才能完成,例如301 Moved Permanently(永久重定向,URL已变更)、302 Found(临时重定向)、304 Not Modified(请求资源未修改,从缓存中获取)。
  • 4xx(客户端错误状态码):表示客户端请求存在错误,例如400 Bad Request(请求参数错误)、401 Unauthorized(未授权,token无效或未携带)、403 Forbidden(禁止访问,无权限)、404 Not Found(请求路径错误,资源不存在)、405 Method Not Allowed(请求方法不允许,例如服务器只允许GET请求,却发起了POST请求)。
  • 5xx(服务器错误状态码):表示服务器端出现错误,例如500 Internal Server Error(服务器内部错误)、502 Bad Gateway(网关错误)、503 Service Unavailable(服务器暂时不可用)、504 Gateway Timeout(网关超时)。

三、TCP三次握手的作用

在前后端数据交互中,HTTP协议基于TCP协议进行传输(HTTPS基于TCP+SSL/TLS),TCP协议是一种面向连接、可靠的传输层协议,而TCP三次握手是建立TCP连接的核心过程,其核心目的是确保通信双方的发送和接收能力正常,为后续可靠的数据传输奠定基础。在实际开发中,网络请求的稳定性与TCP连接密切相关,理解TCP三次握手,能够帮助我们更好地排查网络请求异常(如连接失败、请求超时等)问题。

1. TCP三次握手的核心目的

TCP协议的核心特点是“可靠传输”,而三次握手的作用就是确认通信双方的发送能力和接收能力均正常,避免出现“一方能发送但不能接收,或一方能接收但不能发送”的情况,从而确保后续数据传输的可靠性。具体来说,三次握手需要确认两件事:一是发起连接的一方(客户端)能够正常发送数据,且接收方(服务器)能够正常接收数据;二是接收方能够正常发送数据,且发起方能够正常接收数据。只有这两件事确认完成,TCP连接才能建立,后续的数据传输才能可靠进行。

2. TCP三次握手的具体过程(详细拆解)

TCP三次握手的过程涉及客户端和服务器双方的交互,通过发送特定的报文(SYN报文、ACK报文),完成连接建立,具体步骤如下:

  • 第一次握手(客户端 → 服务器):客户端发起TCP连接请求,向服务器发送SYN(同步)报文。该报文包含一个初始序列号(ISN,随机生成),用于标识后续发送的数据报文的顺序;同时,SYN报文会告知服务器,客户端希望建立TCP连接。此时,客户端处于SYN_SENT状态(等待服务器确认)。
  • 第二次握手(服务器 → 客户端):服务器接收到客户端的SYN报文后,确认客户端的发送能力正常,此时服务器会向客户端发送一个SYN+ACK(同步+确认)报文。该报文包含两部分信息:一是对客户端SYN报文的确认(ACK报文,确认号为客户端ISN+1),表示服务器已成功接收客户端的SYN报文;二是服务器自身的SYN报文(包含服务器的初始序列号ISN'),告知客户端服务器的发送能力正常。此时,服务器处于SYN_RCVD状态(等待客户端确认)。
  • 第三次握手(客户端 → 服务器):客户端接收到服务器的SYN+ACK报文后,确认服务器的发送和接收能力均正常,此时客户端会向服务器发送一个ACK(确认)报文,确认号为服务器ISN'+1,表示客户端已成功接收服务器的SYN报文。此时,客户端和服务器均进入ESTABLISHED状态(连接已建立),TCP三次握手完成,后续即可开始传输数据(如HTTP请求数据)。

3. 为什么需要三次握手,而不是两次或四次?

  • 为什么不是两次握手?假设采用两次握手,客户端发送SYN报文,服务器发送SYN+ACK报文后,就认为连接建立完成。但此时存在一个问题:如果客户端的SYN报文因网络延迟,到达服务器时已经超时,客户端会重新发送SYN报文,建立新的连接;而延迟的SYN报文后续到达服务器,服务器会认为是新的连接请求,发送SYN+ACK报文,此时客户端已经建立了新的连接,会忽略该SYN+ACK报文,服务器则会一直等待客户端的确认,造成资源浪费。三次握手通过客户端的第三次ACK报文,能够确认服务器的SYN+ACK报文已被客户端接收,避免上述问题,确保连接建立的可靠性。
  • 为什么不是四次握手?三次握手已经能够完成“确认双方发送和接收能力”的核心目的,第四次握手没有必要,只会增加连接建立的时间和网络开销,降低传输效率。因此,三次握手是兼顾可靠性和效率的最优选择。

4. 延伸:TCP四次挥手(补充)

与三次握手建立连接相对应,TCP连接的关闭需要经过四次挥手,核心目的是确保双方都已完成数据传输,避免数据丢失。具体过程如下:

  • 第一次挥手:客户端发送FIN(结束)报文,告知服务器客户端已完成数据传输,请求关闭连接,客户端进入FIN_WAIT_1状态。
  • 第二次挥手:服务器接收到FIN报文后,发送ACK报文,确认客户端的FIN报文已接收,服务器进入CLOSE_WAIT状态,此时客户端进入FIN_WAIT_2状态,等待服务器发送FIN报文。
  • 第三次挥手:服务器完成自身的数据传输后,发送FIN报文,告知客户端服务器已完成数据传输,请求关闭连接,服务器进入LAST_ACK状态。
  • 第四次挥手:客户端接收到FIN报文后,发送ACK报文,确认服务器的FIN报文已接收,客户端进入TIME_WAIT状态(等待一段时间,确保服务器接收ACK报文),服务器接收到ACK报文后,关闭连接;客户端等待超时后,也关闭连接,四次挥手完成。

四、学习总结

本次学习重点掌握了前端核心的三个知识点:浏览器渲染机制、GET与POST的区别及HTTP请求结构、TCP三次握手。这三个知识点是前端开发的基础,也是前后端数据交互的核心,相互关联、密不可分——浏览器渲染机制决定了页面的加载速度和用户体验,GET与POST的正确使用规范了前后端数据交互,TCP三次握手则保障了数据传输的可靠性。

在实际开发中,我们需要将这些知识点灵活运用:通过优化浏览器渲染流程,提升页面加载速度和流畅度;根据业务场景正确选择GET或POST请求,规范接口设计;了解TCP三次握手的原理,排查网络请求异常问题。同时,这些知识点也是后续学习前端框架(如React、Vue)、性能优化、网络安全等内容的基础,只有扎实掌握这些基础知识点,才能在前端开发的道路上稳步前行,解决实际开发中遇到的各种问题。