浏览器相关知识1

内容整理来自极客时间李兵老师所讲专栏《浏览器工作原理与实践》
极客时间

  • 浏览器中的进程与线程
    进程:一个进程就是一个程序的运行实例。详细解释就是,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程。
    线程
    1. 线程是不能单独存在的,它是由进程来启动和管理的。
    2. 线程是依附于进程的。
    进程与线程之间的关系
    1. 进程中的任意一线程执行出错,都会导致整个进程的崩溃。
    2. 线程之间共享进程中的数据。
    3. 当一个进程关闭之后,操作系统会回收进程所占用的内存。
    4. 进程之间的内容相互隔离。
    在这里插入图片描述
  • 多进程浏览器 chrome

在这里插入图片描述
在这里插入图片描述
仅仅打开了 1 个页面,为什么有 4 个进程?因为打开 1 个页面至少需要 1 个网络进程、1 个浏览器进程、1 个 GPU 进程以及 1 个渲染进程,共 4 个;如果打开的页面有运行插件的话,还需要再加上 1 个插件进程。

  • TCP协议:如何保证页面文件能被完整送达浏览器?
    1. 数据包要在互联网上进行传输,就要符合网际协议(Internet Protocol,简称 IP)标准。计算机的地址就称为 IP 地址,访问任何网站实际上只是你的计算机向另外一台计算机请求信息。
      在这里插入图片描述
    2. IP 是非常底层的协议,因此,需要基于 IP 之上开发能和应用打交道的协议,最常见的是“用户数据包协议(User Datagram Protocol)”,简称 UDP。UDP 中一个最重要的信息是端口号,端口号其实就是一个数字,每个想访问网络的程序都需要绑定一个端口号。通过端口号 UDP 就能把指定的数据包发送给指定的程序了,所以 IP 通过 IP 地址信息把数据包发送给指定的电脑,而 UDP 通过端口号把数据包分发给正确的程序。
      在这里插入图片描述
    3. 使用UDP协议来传输文件会存在两个问题:
      a. 数据包在传输过程中容易丢失;
      b. 大文件会被拆分成很多小的数据包来传输,这些小的数据包会经过不同的路由,并在不同的时间到达接收端,而 UDP 协议并不知道如何组装这些数据包,从而把这些数据包还原成完整的文件。
    4. TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP特点如下:
      a. 对于数据包丢失的情况,TCP 提供重传机制;
      b. TCP 引入了数据包排序机制,用来保证把乱序的数据包组合成一个完整的文件。
      在这里插入图片描述
    5. 完整的TCP链接过程
      在这里插入图片描述
  • 从输入URL到页面展示,这中间发生了什么?
    1. 判断用户输入的是关键词还是url地址
      1.1 关键词:则调用浏览器默认搜索引擎来进行搜索(加上内容合同url)
      1.2 url地址:则加上协议(如https)合成完整的url
    2. 按下回车,浏览器进程通过IPC(进程间通信)把url传给网络进程(当网络进程接收到url才发起真正的网络请求)
      2.1 此时浏览器导航栏显示loading状态,但是页面还是呈现前一个页面,这是因为新页面的响应数据还没有获得
    3. 网络进程接收到url后,先查找有没有本地缓存
      3.1 有缓存,则拦截请求,直接返回200的缓存的资源。
      3.2 无缓存,则进入真正的网络请求过程。
    4. 网络进程请求DNS返回域名所对应的IP和端口号
      4.1 首先获取域名的IP,系统会自动从hosts文件中寻找域名对应的IP地址,若找到则和服务器建立TCP链接。
      4.2 若没找到,则进行DNS解析找到IP地址。
      4.3 若之前DNS数据缓存服务缓存过当前域名信息,就会直接返回缓存信息,否则,发起请求获取域名解析出来的IP和端口号,如果没有端口号,http默认80端口,https默认443端口,若还是https请求,则还需要建立TLS链接。
    5. Chrome有个机制,同一域名同时最多只能建立6个TCP链接,若同一个域名下同时有10个请求发生,那么其他4个排队等待,若小于6个,则直接建立。
    6. TCP三次握手建立连接,http请求加上TCP头部-包括端口号,目的程序款口号和用域校验数据完整性的序号,向下传输
    7. 网络层在数据包上加上IP头部-包括源IP地址和目的IP地址,继续向下传输到底层
    8. 底层通过物理网络传输给目的服务器主机
    9. 目的服务器主机传输层收到数据包,解析出IP头部,识别数据部分,将解开的数据包向上传输到应用层
    10. 应用层http解析请求头和请求体
      10.1 若需要进行重定向,则http直接返回数据状态码为301,302,同时在请求头的Location字段中附上重定向地址,浏览器根据code和location进行重定向操作。
      10.2 若不需要重定向,首先服务器会根据请求头中的if-None-Match的值来判断请求的资源是否被更新了,若没有更新则返回304状态码,相当于告诉浏览器之前的缓存还可以继续使用,就不返回新数据了。
      10.3 否则,则返回新数据,200状态码,并且如果想要浏览器缓存数据的话,就在响应头中加入字段:Cache-Control:Max-age=2000(缓存时间2秒)
    11. 响应头中的Content-Type属性告诉了浏览器服务器返回的数据是什么类型,若返回text/html则为网页类型,则浏览器准备渲染进程来渲染页面了,若为下载类型比如application/octet-stream,则是下载类型,请求会交给浏览器的下载进程
    12. 数据传输完成,TCP四次挥手断开连接,若浏览器或者服务器在http头部加上Connection:Keep-Alive属性的话,则TCP就保持一直连接,保持TCP连接可以节约下次建立连接的时间。
    13. 浏览器进程获取到通知,根据当前页面B是否从页面A打开的并且和页面A是否是同一个站点(根域名和协议医院就被认为是同一个站点),若满足上述条件,则复用之前网页的进程,否则,重新创建一个单独的渲染进程
    14. 浏览器发出“提交文档”的消息给渲染进程,渲染进程收到消息后,会和网络进程建立传输数据的“管道”,文档数据传输完成后,渲染进程会返回“确认提交”的消息给浏览器进程。
    15. 浏览器收到“确认提交”消息后,会更新浏览器的页面状态,包括了安全状态,地址栏的URL,前进后退的历史状态,并更新web页面,此时web页面是上个页面内容或者空白页面。
    16. 此时渲染进程这个沙盒会对文档进行页面解析和字子资源加载,HTML会通过HTML解析器转成DOM tree,css按照css规则和css解析器转换成CSSOM tree 两个tree结合,形成render tree,并且通过Layout布局方案计算出每个元素具体的宽高颜色位置,结合起来,开始绘制,最后显示新页面。
  • HTML,CSS,JS如何变成页面的?

    渲染进程中的流程:
    -》(DOM构建DOM树 (浏览器无法识别 则会将html文件转换成dom树结构体)
    -》(StyleSheets样式计算 (同理 浏览器无法识别css样式 则会转换为styleSheets结构)
    1. 把css转换为浏览器能够理解的结构 其中css来源如下:
    a. 通过link标签链接外部地址
    b. 标签体内容
    c. 行内样式
    2. 转换样式表中的属性值,使其标准化
    a. 比如em变成px
    b. 比如颜色blue变成rgb
    c. 比如bold变成700
    3. 计算出DOM树中每个节点的具体样式
    a. 涉及css的继承规则(每个子元素都会继承其父元素样式)
    b. 涉及css的层叠规则(合并来自多个源的属性值)
    -》(Layout布局阶段
    1. 创建布局树(由dom树和ComputedStyle计算样式 再生成布局树)
    a. Dom树中包含了不可见元素,比如head标签,display:none属性设置,所以还会额外的构建一颗可见元素的布局树
    b. 遍历dom树中所有可见节点,并加到布局树中,忽略其他不可见元素
    2. 布局计算
    在这里插入图片描述
    -》(Layer分层
    1. 渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)
    2. 浏览器的页面实际上被划分成了很多图层,这些图层叠加后合同了最终的页面
    3. 并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层
    4. 何时创建为图层:
    a. 拥有层叠上下文属性的元素会被提升为单独的一层(如:明确定位属性的元素;定义透明属性的元素;使用css滤镜的元素)
    b. 需要裁剪(clip)的地方会被创建图层(如文字超过div高度;出现滚动条)
    -》(Drawing绘制
    1. 浏览器会把图层拆分成很多小的绘制指令
    2. 形成一个绘制列表
    以上均由渲染进程中的主线程来完成,接下来“主线程”将“commit”提交至“合成线程”来实际完成绘制
    -》(Tile分块
    1. 屏幕上页面的可见区域就叫视口(ViewPort)
    2. 由于用户只看的见这部分内容,所以浏览器的合成线程会将图层划分为图块(tile),这些图块大小通常是256x256或者512x512
    -》(Raster光栅化
    1. 合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图
    2. 渲染进程维护了一个栅格化的线程池
    3. 通常栅格化都是GPU来加速生成的,该操作叫做快速栅格化,生成的位图保存在GPU内存中
    -》(Display合成显示
    1. 在所有图块都被光栅化后,合成线程发出“DrawQuad“命令提交给”浏览器进程“
    2. 浏览器进程有个viz组件,根据DrawQuad命令,将其内容绘制到内存中,最后将内存显示在屏幕上
    总结上面的所有流程:

  1. 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。
  2. 渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。
  3. 创建布局树,并计算元素的布局信息。
  4. 对布局树进行分层,并生成分层树。
  5. 为每个图层生成绘制列表,并将其提交到合成线程。
  6. 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  7. 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。
    在这里插入图片描述
    如何理解“重绘“、”重排“、”合成“?
    1. 重排)更新了元素的几何属性
      在这里插入图片描述
      如果你通过 JavaScript 或者 CSS 修改元素的几何位置属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。
    2. 重绘)更新元素绘制属性
      在这里插入图片描述
      如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。
    3. 合成
      在这里插入图片描述
      比如使用了transform来实现动画效果,会避开重绘和重排,合成是非主线程来完成。
  • 变量提升:javascript代码是按顺序执行的吗?
    1. Js代码执行流程:
      变量和函数声明在代码里得位置是不会改变得,而且是在编译阶段被js引擎放入内存中;意思就是一段js代码在执行前需要被js引擎编译,编译完成后,才会进入执行阶段。
      在这里插入图片描述
    2. 编译阶段:
      主要就是做2件事,第一:变量提升部分代码;第二:执行部分的代码。
      在这里插入图片描述
      编译过后,会产生两部分内容:执行上下文(变量环境&词法环境等)和可执行代码。
    3. 一段代码如果定义了两个相同名字的函数,那么最终生效的是最后一个函数。
      showName()
      var showName = function(){console.log(2)}
      showName()
      function showName(){console.log(1)}
      showName()
      3.1 首先是编译阶段的 变量提升 则
      Var showName = undefined
      function showName(){console.log(1)}
      3.2 然后是可执行代码阶段
      ShowName()
      showName = function(){console.log(2)}
      showName()
      showName()
      3.3
      即第一个showName函数执行 打印1
      接下来将showName变量赋值成函数打印2
      即后面showName函数都将打印2
  • 为什么js代码执行会出现“栈溢出“?
    1. 调用栈就是用来管理函数调用的关系的一种数据结构。
    2. 首先会对全局创建全局执行上下文,如果执行到函数的时候,并会创建该函数的执行上下文和可执行代码。
    3. 在执行上下文创建好后,JavaScript 引擎会将执行上下文压入栈中,通常把这种用来管理执行上下文的栈称为执行上下文栈,又称调用栈。
      在这里插入图片描述
    4. 当函数返回的时候,当前函数的执行上下文就会从栈顶弹出,调用栈是 JavaScript 引擎追踪函数执行的一个机制。
    5. 如何查看当前js引擎栈有多深:
function computeMaxCallStackSize () {
   try {
         return 1 + computeMaxCallStackSize()
   } catch (e) {
     // Call stack overflow
     return 1
   }
}
computeMaxCallStackSize()
  1. 如何避免调用栈溢出:
    • 循环代替递归
    • 尾递归优化(safari浏览器支持,其他欠缺)
    • 事件驱动 的特性,使用 “setTimeout”、 “nextTick” (利用事件循环机制)
  2. 总结:
    7.1 每调用一个函数,JavaScript 引擎会为其创建执行上下文,并把该执行上下文压入调用栈,然后 JavaScript 引擎开始执行函数代码。
    7.2 如果在一个函数 A 中调用了另外一个函数 B,那么 JavaScript 引擎会为 B 函数创建执行上下文,并将 B 函数的执行上下文压入栈顶。
    7.3 当前函数执行完毕后,JavaScript 引擎会将该函数的执行上下文弹出栈。当分配的调用栈空间被占满时,会引发“堆栈溢出”问题。
  • 作用域链和闭包
    1. 作用域链
      在每个执行上下文的变量环境中,都包含了一个外部引用,用来指向外部的执行上下文,我们把这个外部引用成为outer,通过作用域查找变量的链条称为作用域链,作用域链是通过词法作用域来确定的,而词法作用域反映了代码的结构。
      在这里插入图片描述
    2. 词法作用域
      a. 词法作用域就是指作用域是由代码中函数声明的位置来决定的,所以词法作用域是静态的作用域,通过它就能够预测代码在执行过程中如何查找标识符。
      b. 词法作用域是代码编译阶段就决定好的,和函数是怎么调用的没有关系。
    3. 闭包
      a. 在js中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。
      b. 如果该闭包会一直使用,那么它可以作为全局变量而存在,但如果使用频率不高,而且占用内存又比较大的话,那就尽量让它成为一个局部变量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值