一、 在浏览器输入网页地址,按下回车键,发生了什么?
从输入一个 URL,到页面加载完成的所有步骤。
DNS 查找
我们在浏览器地址栏输入 URL 并回车之后,浏览器会进行 DNS 查找,把域名转换为真实的 IP 地址,根据 IP 地址找到提供网站内容的服务器。
TCP / TLS 握手
在找到服务器之后,浏览器会通过 TCP 握手机制跟服务器建立连接,而现在大部分服务器传输都是基于 HTTPS 协议的,那么会多一步 TLS 握手,建立加密的隧道,保证数据传输不被监听和篡改。
发送 HTTP 请求
建立了浏览器和服务器之间的链接之后,浏览器会发起 HTTP 或 HTTPS 请求,来获取服务器响应,一般对于网站来说,响应就是服务器会返回 HTML 网页代码。这里在接收服务器响应的时候,有一个 slow start 机制,受制于 TCP 连接的限制,浏览器会先收到前 14KB 的数据,后续才会慢慢增加传输速度,下载其它文件,所以对于服务器来说,能够在这 14KB 的数据里,完整的展现网站,就变得很重要了。
浏览器渲染过程
在收到 HTML 代码之后,浏览器开始渲染网页,这里一共有 5 步,这 5 步统称为关键渲染路径(Critical Rendering Path)。
1. 构建 DOM 树
第一步是解析 HTML 并构建 DOM 树,DOM 树是 HTML 文档在浏览器中的对象表示,可以使用 JavaScript 来操作它。浏览器在解析 HTML 的时候是顺序执行的,并且只有一个主线程负责解析,如果遇到 <script /> 标签,那么浏览器会加载 javascript 文件并执行里边的代码,这个时候主线程会暂停解析 HTML,只有 js 代码执行完之后才会继续。对于图片和 CSS 等文件,或者设置了 defer 或 async 的 script 标签,它们不会影响主线程,而是会异步的加载。
另外,浏览器有一个预扫描(Pre Scanner)线程,它会扫描 HTML 代码,提前把 css 文件、字体以及 js 文件下载下来,加速文件的下载,并且不影响主线程。
2. 构建 CSSOM 树
第二步是构建 CSSOM 树,CSSOM 是 CSS 在浏览器中的对象表示,也是树状结构。
3. 合并 DOM 和 CSSOM
第三步,浏览器会从 DOM 的根节点开始,合并 CSSOM 中的样式到每个节点中,形成一棵渲染树(Render Tree)。
4. 布局
第四步,生成渲染树之后,浏览器会根据样式,计算每个可见节点(没有设置 display 为 none 的节点)的宽高和位置等,对所有节点进行布局规划。对于像图片这样的节点,如果没有指定宽高,那么浏览器会先忽略它的大小,在图片加载完成之后,浏览器会根据图片的宽高和位置,再次计算受影响的节点的大小和位置,这个过程叫作回流(Reflow)。
5. 绘制
第五步,在第一次布局完成之后,浏览器会真正的把节点和节点的样式,例如背景、阴影、边框等绘制到屏幕上,这个要求过程必须要十分的快速,否则会影响动画和交互的性能。如果之前布局发生了【回流】,也就是加载了像图片这样的节点之后,浏览器还会发生重绘(Repaint),把变化的布局重新绘制到屏幕上。在绘制期间,也可能会有组合(Composition)发生,因为在渲染节点时,可能会产生新的图层,例如
交互
在上面五步完成之后,设置了 defer 或 async 的 JavaScript 文件开始加载并执行,完成之后整个网页就加载完成并可以和用户进行交互了。
小结
这个就是浏览器的工作原理,从输入一个 URL 到页面加载完成需要经过:DNS 查找、TCP/TLS 握手,发送 HTTP 请求,构建 DOM 树、构建 CSSOM 树、合并 DOM 和 CSSOM、布局、绘制这几大步骤。
二、垃圾回收
只要是已经经历过一次Scavenge算法回收的就可以晋升为老生代内存的对象
(1) 新生代垃圾回收
内存空间比较小的,使用Scavenge算法回收
(2) 老生代垃圾回收
采用标记清除和标记整理的策略进行老生代内存中的垃圾回收
- 首先,它会遍历堆上的所有的对象,分别对他们打上标记,然后在代码执行过程结束之后,对使用过的变量取消标记。在清除阶段,就会把还有标记的进行整体清除,从而释放内存空间。
- 通过标记清除产生的内存碎片,需要通过标记整理进行解决
三、事件循环Eventloop
1. 宏任务和微任务
(1) 宏任务(消息队列中的任务成为宏任务)
- 渲染事件(比如解析DOM、计算布局、绘制)
- 用户交互事件(比如鼠标点击、滚动页面、放大缩小等)
- setTimeout、setInterval、setImmediate(Node.js)等
- 网络请求完成、文件读写完成事件
(2) 微任务
Object.observe、Promise、MutationObserver监控DOM节点的变化、process.nextTick(Node.js)
- 微任务和宏任务是绑定的,每个宏任务在执行时,会创建自己的微任务队列
- 微任务的执行时长会影响当前宏任务的时长
- 在一个宏任务中,分别创建一个用于回调的宏任务和微任务,无论什么情况下,微任务都早于宏任务执行
(3) 宏任务的应用场景
监听DOM变化:
在每次DOM节点发生变化的时候,渲染引擎将变化记录封装成微任务,并将微任务添加进当前的微任务队列中
MutationObserver采用了“异步 + 微任务”的策略
- 通过异步操作解决了同步操作的性能问题
- 通过微任务解决了实时性的问题
2. 浏览器的event loop
- 所有的同步任务都在主线程上执行,形成一个执行栈。
- 除了主线程之外,还存在一个任务队列,只要异步任务有了运行结果,就在任务队列中植入一个时间标记。
- 主线程完成所有任务(执行栈清空),就会读取任务队列,选择最先进入队列的宏任务,执行其同步代码直至结束。
- 检查是否有微任务,如果有则执行直到微任务队列为空。
- 开始下一轮循环,执行宏任务中的一些异步代码