浏览器抓取网页(一个HTTP请求报文的Content-Type网络进程会影响后续流程)
优采云 发布时间: 2022-02-24 13:16浏览器抓取网页(一个HTTP请求报文的Content-Type网络进程会影响后续流程)
HTTP 请求消息由四部分组成:请求行、请求头、空行和请求实体。浏览器端开始构造请求行、请求头和请求体等信息,将浏览器的一些基本信息和cookie等信息添加到请求头中,然后将构造好的请求消息发送给服务器。
(5)服务器处理请求并返回数据
服务器收到请求消息后,会根据消息信息生成响应头、响应行、响应体等数据。网络进程收到响应头和响应行后,就可以解析响应头的内容了。网络进程解析响应头信息后,会转发给浏览器进程进行处理。
影响后续流程的两条信息:
响应行中的 301/302 状态代码执行重定向
在解析响应头的时候,还需要注意一个重定向操作:如果响应行信息是301/302,则表示需要重定向到另一个URL地址。这时,网络进程会从响应头中读取位置字段。值并重新发起 http/https 请求。
响应标头中的 Content-Type
网络进程会首先根据响应头中的 Content-Type 字段判断服务器返回什么类型的资源文件,因为不同的资源类型对浏览器的处理方式不同:
(6)断开连接:TCP 挥手四次
核心思想:TCP为什么需要四次挥手?原因是:释放连接是释放客户端到服务器和服务器到客户端的双向连接。具体来说就是确认客户端和服务端的发送和接收功能都关闭了;
3. 准备渲染进程4. 提交文档 当浏览器进程收到网络进程的响应头数据时,向渲染进程发送“提交文档”消息;渲染进程收到“提交文档”消息后,会与网络进程建立“管道”传输数据;文档数据传输完成后,渲染进程会向浏览器进程返回“确认提交”消息;浏览器进程收到消息后,开始更新页面状态,包括安全状态、地址栏URL、前进后退历史状态、更新网页等。
5. 浏览器解析并渲染页面
一旦文档被确认提交,渲染过程就开始解析资源并渲染页面。注意浏览器的渲染页面并不是等所有资源加载完毕才开始渲染,而是边解析边渲染,这也是为什么有些页面会先将文本呈现给用户,然后再加载图片等资源之后。
详细渲染过程见第4点
四、浏览器是如何工作的?
以chrome浏览器为例
浏览器的结构大致可以分为:用户界面、浏览器引擎、渲染引擎
4.1 不同的浏览器内核
IE:三叉戟
火狐:壁虎
Safari:Webkit
Chrome/Opera/Edge:基于 Webkit 改造的 Blink
4.2 浏览器的多进程结构
今天的浏览器是多进程结构,而早期的浏览器是单进程结构
早期浏览器单进程结构:
单进程结构提出了许多问题:
不稳定:其中一个线程卡住可能会导致整个进程出现问题。例如,如果您打开多个选项卡,其中一个卡住的选项卡可能会导致浏览器无法运行。不安全:数据可以在浏览器之间共享,那么js可以自由访问浏览器中的所有数据进程不流畅:一个进程需要负责的事情太多,导致效率低下
因此,当今的浏览器选择了多进程结构。根据不同的进程功能,浏览器可以分为:
4.3 chrome的4个进程模型
Chrome 共有 4 种进程模型,不同的进程模式对 tab 进程的处理方式不同。
4.4 在浏览器地址栏中输入内容时,浏览器内部会发生什么?(详细请看三) 1.用户输入URL阶段+DNS+请求数据+处理返回数据
当我在地址栏中输入内容时,浏览器进程的UI线程会捕获我输入的内容,如果输入了URL,UI线程会启动一个网络线程(这是在浏览器进程中,而不是网络进程,网络过程 当你发起http请求请求DNS进行域名解析,然后开始连接服务器获取数据,如果输入关键词,浏览器就知道你要使用搜索功能,所以它将使用默认配置的搜索引擎进行查询;
网络线程获取到数据后,会通过Safe Browsing(Google内部的一个站点安全系统,通过检测站点数据来判断是否安全)检查该站点是否为恶意站点,如果是,则发出警告页面会提示。告诉我这个站点存在安全问题,浏览器会阻止我访问,但我可以强制它继续。当返回的数据准备好并且安全检查通过时,网络线程会通知UI线程我准备好了,轮到你了。
2. 渲染页面阶段
然后UI线程会创建一个渲染器进程来渲染页面,浏览器进程将数据通过IPC管道传递给渲染器进程,进入渲染进程。渲染器进程接收数据(html(通常收录 css、js 和图像资源))。
渲染器进程的核心任务:将html、js、css、image等资源渲染成用户可以交互的网页。渲染器进程的主线程解析html并构造DOM结构。
HTML首先通过Tokeniser进行token化,输入的html内容通过词法分析解析成多个标签,根据识别出的标签进行DOM树的构建。在DOM树的创建过程中,会创建一个document对象,然后会创建一个以document为根节点的文档。DOM 树。
需要注意的是,html代码中经常会引入一些额外的资源,比如js、css、图片等。图片、CSS等资源需要从网络下载或者直接从缓存中加载。这些资源不会阻塞 html 的解析,因为它们不会影响 DOM 的生成。当解析过程中遇到脚本标签时,会停止对html的解析。,然后加载、解析并执行js,因为js可能会改变当前页面的html结构,有可能在js中添加或删除节点等操作会影响html的结构,所以script标签必须放在合适的位置,或者使用async或者defer属性异步加载js。
html解析完成后,我们会得到一个DOM Tree,但是此时我们并不知道DOM树上的每个节点是什么样子的。这是主线程需要解析 CSS 并确定每个 DOM 节点的计算样式。
在了解了每个节点的 DOM 结构和样式之后,就需要知道每个节点在页面上的位置,即节点的坐标以及节点需要占用多少区域。这个阶段称为布局布局。线程通过遍历 dom 和计算出来的样式生成 Layout 树。布局树上的每个节点都记录了 x、y 坐标和边框大小。
需要注意的是,DOM树和Layout树并不是一一对应的。如果设置了 display: none,则不会出现在布局树上。如果在 before 伪类中添加了一个带有 content 值的元素,就会出现 content 中的内容。不会出现在Layout树上的DOM树上,因为DOM是通过html解析得到的,不关心样式,而Layout树是DOM树结合CSSOM树生成的。
只知道元素的大小、形状和位置是不够的。我们还需要知道绘制节点的顺序。毕竟像z-index这样的属性会影响节点绘制的层次关系。如果我们按照 DOM 层次绘制,就会导致渲染不正确。所以为了保证屏幕上显示正确的层级,主线程遍历Layout树创建了一个绘制记录表(Paint Record),记录了绘制的顺序。这个阶段称为绘画。
知道了节点的绘制顺序,是时候将这些信息转换成像素并在屏幕上显示了。这种行为称为光栅化。
Chrome 早期的光栅化方案只是对用户可见区域的内容进行了光栅化。当用户滚动页面时,更多的内容被光栅化以填补缺失的部分。这种方案会导致显示延迟。现在chrome的光栅化过程称为堆肥。Composting是一种将页面的每一部分分成多个层,分别进行光栅化,在合成器线程技术中分别合成页面的技术。简单来说,就是将页面的所有元素按照一定的规则划分成图层,对图层进行光栅化处理,然后只将可视区域的内容组合成一帧显示给用户。
主线程遍历布局树生成层树。当层树生成并确定了绘制顺序时,主线程将此信息传递给合成器线程。
合成器线程对每一层进行光栅化,并且由于层可以与页面的整个长度一样大,因此合成器线程将它们切成许多块并将每个块发送到光栅化器线程。
光栅化线程光栅化每个图块并将它们存储到 GPU 内存中
当瓦片被光栅化时,合成器线程将采集称为“绘制四边形”(来自网格线程)的瓦片信息,这些信息记录瓦片在内存中的位置以及它们在页面上的绘制位置。瓦片的信息,合成器线程根据该信息生成合成器帧(Compositor Frame)
然后这个合成器帧通过IPC传给浏览器进程,然后浏览器进程把合成器帧传给GPU,然后GPU渲染到屏幕上,然后就可以看到页面的内容了。
但是当页面发生变化时,会生成一个新的合成器框架,并重复上述动作
3. 回流和重绘
重排重绘会占用主线程,而js也在主线程上运行,所以会出现抢占执行时间的问题。在一帧内完成布局绘制后还有时间时,js会获得使用主线程的权利。如果js的执行时间过长,js会在下一帧开始时没有及时返回到主线程,导致下面的一帧*敏*感*词*没有按时渲染,页面*敏*感*词*就会卡住。
有什么优化方法吗?? ?
当然有!!
4. 优化回流和重绘导致页面*敏*感*词*卡顿的问题
第一个是通过 requestAnimationFrame() API。每一帧都会调用这个方法。通过API回调,我们可以将JS运行的任务分成更小的任务块(划分为每一帧),并在每一帧时间用完前暂停js执行并返回主线程,这样主线程就可以布局和下一帧开始时准时绘制
第二种光栅化不占用主线程,只运行在合成器线程和网格线程中,也就是说不会用js抢占主线程。CSS有一个*敏*感*词*属性transform,这个属性实现的*敏*感*词*是不会通过的。布局和绘图直接运行在合成器线程和网格线程上,因此不会受主线程中的 JS 影响。更重要的是,由于transform实现的*敏*感*词*不需要经过布局绘制样式计算等操作,因此节省了大量的计算时间
5. 渲染管道
当网络进程向渲染进程提交资源时,渲染进程就会开始渲染页面。浏览器的渲染机制非常复杂,所以渲染会分为很多子阶段。输入的 HTML、CSS、JS 以及图片等资源都会经过这些阶段,最终输出的像素会显示在页面上。处理流程称为渲染管道。
根据渲染的时间顺序,我们将浏览器的渲染时间段划分为以下几个子阶段。需要注意的是,每个子阶段的输出都会作为下一个子阶段的输入,这也符合实际工厂流水线场景。
6. 渲染过程总结
1、渲染过程将HTML内容转化为可读的DOM树结构
2、渲染引擎将CSS样式表转换成浏览器可以理解的样式表,并计算出DOM节点的样式
3、创建布局树并计算元素的布局信息。
4、对布局树进行分层并生成分层树。
5、为每一层生成绘制列表并将它们提交到合成线程。
6、合成线程将图层划分为瓦片,并将瓦片转换为光栅化线程池中的位图。
7、合成线程向浏览器进程发送绘图平铺命令DrawQuads
8、浏览器进程根据 DrawQuads 消息生成页面并显示在监视器上。
五、JS的运行原理(重点:JS引擎)5.1 js继承了以下四种语言的特点
5.2个奇怪的js
5.3 JIT
js是动态类型语言,但这也导致我们在运行前无法知道变量的类型。只有在运行过程中才能确定每个变量的类型,这使得js无法在运行前编译更快、更底层。语言代码(机器代码)。尽管如此,js 执行起来还是很快的,这是为什么呢?? ? 这是因为当前的 JavaScript 引擎使用了一种技术:Just-In-Time Compilation,简称 JIT,就是在运行阶段生成机器码,而不是提前生成。JIT 在运行代码的同时生成机器码。
5.4 JS 引擎
js 是一种高级语言。在被计算机CPU执行之前,需要将JS转换为低级机器语言,通过程序执行。该程序称为 JavaScript 引擎。JavaScript 有很多引擎:
js引擎执行的一般流程(new V8)首先通过解析器将JS源码解析成抽象语法树AST;然后通过解释器将AST编译成字节码btyecode,字节码是一个中间表示跨平台的,不同于最终的机器码。字节码是平台无关的,可以运行在不同的操作系统上;字节码最终通过编译器(compiler)生成机器码,由于不同的处理编译平台所使用的机器码会是不同,所以编译器会根据当前平台编译对应的机器码
5.5 以V8为例了解JS引擎编译和JS优化
JS的引擎是V8,node.js的运行环境也是V8引擎,electron的底层引擎也是V8.
简单地说,V8 是一个 C++ 程序,它接收 JavaScript 代码,编译代码然后执行。编译后的代码可以在多个操作系统和多个处理器上运行。
V8主要负责工作:
1. 早期的 V8 引擎
JS 引擎在编译和执行 JS 代码时使用了三个重要的组件:
但在 V8 版本之前,V8 引擎没有解释器,而是有两个编译器。编译过程如下:
JS源码由解析器解析生成抽象语法树AST;那么 Full-codegen 编译器直接使用 AST 编译机器码,无需任何中间转换。Full-codegen 编译器也称为基准编译器,因为它生成基准未优化的机器代码。这样做的好处是我第一次执行JS时,直接使用高效的机器码,因为没有生成中间字节码,所以不需要解释器;代码运行一段时间后,V8 中的分析器线程已经采集到足够的数据来帮助另一个编译器 Crankshaft 进行代码优化,然后重新启动需要优化的代码解析并生成 AST,
这个设计的初衷是好的,减少了AST到字节码的转换时间,提高了JS在外部浏览器中的执行性能,但是也存在很多问题:(文章地址:)
所有提议的新 V8 架构
2. 在新的V8架构中,JS源代码首先被解析器解析,生成抽象语法树AST;但是在获得AST之后,添加了解释器Ignition,AST通过Ignition生成字节码bytecode,此时AST被清空。如果被丢弃,则释放内存空间;生成的字节码由解释器直接执行,生成的字节码将作为基准执行模型。字节码更简洁,生成的字节码大小相当于等效基准机器码的25%~50%左右;在代码的持续运行过程中,解释器采集可用于优化代码的信息,例如:变量的类型,哪些函数执行效率更高,
3. 新V8引擎在处理JS过程中的一些优化策略。如果不调用函数知识声明,则不会解析函数生成AST,也不会生成字节码;如果该函数只被调用一次,Ignition生成字节码后会直接被解释执行。Turbofan不会优化和编译,因为函数执行时需要ignition来采集类型信息,这就要求函数至少执行一次,Turbofan才能优化;如果该函数被多次调用,可能会被识别为热点函数。当 Ignition 采集的类型信息确定后,Turbofan 会将字节码编译成优化的机器码,以提高代码的执行性能。
4. 去优化
必须注意的是:
在某些情况下,优化的机器码可能会被反转为字节码。这个过程称为去优化。这是因为 JS 是一种动态类型的语言,这会导致 Ignition 采集到的信息不正确。
举个例子:比如有个sum(x,y)函数,JS引擎不知道x和y是什么类型的参数,但是在后面的多次调用中,传入的x和y都是整数,而sum 函数 当函数被识别为非热点时,解释器将采集到的类型信息和字节码发送给编译器,因此编译器生成的优化机器码假设 sum 函数的参数是整数,然后遇到函数调用直接使用速度更快的机器码,但是如果在使用sum函数的时候传入一个字符串,就会出现问题。机器码不知道如何处理字符串的参数,所以需要对字节码进行反优化回退,由解释器解释执行。
5. 新 V8 架构的好处
除了解决旧架构的问题,新架构还带来了好处:
由于一开始不需要编译成机器码,而是生成中间层的字节码,而且生成字节码的速度比机器码快很多,所以初始化、解析和执行JS的时间网页的长度被缩短,网页可以更新。快速加载;在生成优化的机器码时,不需要从源代码重新编译,而是使用字节码,当需要反优化时,只需要返回字节码的解释和执行。
新模块的性能得到了更好的提升,功能模块的功能页面更加清晰,为以后JS的新功能和优化页面铺路
六、跨域问题6.1 跨域问题的原因
由于浏览器同源策略限制。同源策略(Sameorigin Policy)是一种约定。它是浏览器的核心和最基本的安全策略。如果缺少同源策略,可能会影响浏览器的正常功能。可以说,Web是建立在同源策略的基础上的,而浏览器只是同源策略的一个实现。同源策略防止来自一个域的 JavaScript 脚本与来自另一个域的内容进行交互。所谓同源(意思是在同一个域中)是指两个页*敏*感*词*有相同的协议、主机和端口。
6.2 跨域的概念
当请求url的协议、域名和端口中的任何一项与当前页面url不同时,即为跨域
当前页面url是否被请求页面url是否跨域
不
同源(同一个协议、同一个域名、同一个端口号)
跨域