浏览器从输入url到页面展示的过程

1,147 阅读5分钟

碎碎念

写在深夜,抬头看着月亮。感慨万分。一句话-心中有路,便有路直通远方。

浏览器多进程架构

我们都知道js是单线程的,那么浏览器多进程的架构我们可能不太熟悉,这里简述一下。

  • 浏览器进程-主进程

    • ui线程 - 控制ui,例如输入框、按钮
    • 网络线程 - 负责资源下载
    • 存储线程 - 负责本地缓存的访问
  • 渲染进程-浏览器内核

    • js线程 - js引擎,负责js的执行。
    • 渲染线程 - 负责ui的渲染,和js线程互斥,一运行一挂起。
    • 事件触发线程 - 负责事件管理,例如点击事件,该线程就负责把对应的任务添加到任务队列。
    • 定时器线程 - 负责定时器的执行,例如setTimeout,其实就是BOM提供的一个接口。
    • 异步资源请求线程 - 负责ajax请求,然后通过回调触发事件触发线程。
  • gpu进程-负责浏览器和gpu的通信

  • 第三方插件进程-主要是浏览器插件运行进程

过程

请求行构建

ui线程把输入的url交给网络线程,网络线程构建请求行,浏览器准备发起请求。
请求行的格式: <请求方法> <请求路径> <协议>。

查找强缓存

什么是强缓存

强缓存其实就是存储在本地的资源-硬盘或者内存中,如果命中缓存,则直接使用缓存资源,如果没有则下一步。

强缓存方式

  • expire 通过expire设置失效日期,与本地系统的时间对比,如果用户修改本地时间就可能导致缓存失效,这是一个缺点,所以就诞生了max-age。
  • cache-control: 'max-age=' max-age优先级比expire优先级高,设置缓存多少秒失效。

DNS解析

域名其实就是ip地址的别名,为了让人们使用更方便。DNS解析其实就是把域名解析成ip地址。解析过程:

  • 浏览器缓存,这里可以解答为什么301重定向后,不需要再访问旧地址再跳到新地址。
  • 系统缓存,这里主要是hosts文件
  • 根域DNS服务器
  • 一级域名服务器
  • 二级域名服务器
  • ...

建立tcp连接

这里不做过多叙述,要明白几个点:

  • 三次握手的过程
  • 为什么要三次 建立tcp连接其实很耗时间的,所以诞生了很多优化方式,例如pre-connect等,优化方式,之后会再写一篇文章。

发送请求,接受响应

这里主要是在建立好的tcp连接中发送请求报文,以及接受响应报文。请求报文三部分组成:请求行,请求头、请求体。响应报文同理。

查找协商缓存

什么是协商缓存

image.png

协商缓存和强缓存的区别

协商缓存每次都要发起请求,然后服务器判断资源是否更新,如果没有更新则直接返回304,如资源更新了则直接返回资源,与cache-control:no-cache搭配使用。
强缓存则不需要每次都发起请求,通过expire或者max-age判断缓存是否失效,不失效则直接使用。

协商缓存的方式

last-modified

当初次请求时,服务端给响应头加last-modified:<修改时间>字段,当再次请求时浏览器会把last-modified的值加到if-modified-since上,服务端再根据该字段判断是否资源更新。

缺点
  • 值粒度为秒,如果在1秒内修改资源,那么缓存还是不会更新的。
  • 因为值时修改资源的时间,那么如果我只是修改资源,但这里修改没有修改内容,那么缓存失效。

ETAG

ETAG优先级比前者高,ETAG时内容签名,我们可以认为是根据内容生成的hash,当初次请求时,服务端给响应头加ETAG:字段,当再次请求时浏览器会把etag的值加到if-none-match上,服务端再根据该字段判断是否资源更新。 ETAG解决了前者的很多问题点。

tcp连接断开

这里不做过多叙述,要明白几个点:

  • 四次挥手的过程
  • 为什么要四次

渲染

生成dom tree

我们获取的是字节形式的html内容,所以我们要经过以下几步形成dom tree

  • 字节按utf8编码成字符串
  • 字符串词法分析,标记化
  • 标记语法分析形成dom节点,包含节点的属性和规则
  • 按节点之间的关系形成dom tree

生成cssom tree

与上一步同理,不过这里有几点要明白

  • 属性转换,例如:blue -> RGBA,blue其实有点像指令集的助记符。
  • 确定每个标签的最终样式

生成布局树

我们确定了每个节点-dom tree,以及每个节点的样式-cssom tree,接下来我们就要确定节点的几何属性,所以 dom tree + cssom tree 形成布局树,布局树要注意几点:

  • display:none的元素不在布局树上
  • 忽略head

生成图层树

我们要对特定的节点形成对应的图层,因此根据对应的规则+布局树形成图层树。为什么要形成对应的图层,个人推断应该防止渲染爆炸。这里的规则可以看一下MDN。这里不做过多描述。

生成绘制列表

把图层拆成一个个绘制指令,再按绘制顺序形成绘制列表。然后渲染主线程把绘制列表交给合成线程。

栅格化

栅格化其实就是合成线程把图层拆成图块,再把视口附近的图块转换成位图。

合成与显示

当栅格化完成后,合成线程发出drawquad命令,浏览器进程有个viz组件会监听,然后会把页面写入内存再显示到页面上,