乐趣区

关于前端:万字好文带你了解浏览器原理

背景

为什么要理解浏览器原理?

当面试官问你输出 url 到渲染产生了什么这种问题你手足无措?

页面中到底能承载多少个元素,取决于什么条件?如果一个页面在 2s 内打不开,你应该如何优化?

DOM 是什么,javascript 操作的是 DOM 还是 html?

回流和重绘又是什么?浏览器架构是什么样的?

当你可能细化的理解整个了浏览器工作原理的时候,你就能很好的解决这些问题

到底什么是浏览器

浏览器咱们罕用的有谷歌 IE Safari 火狐等等,目前开发者心中的浏览器只有一个,那就是谷歌浏览器,它的市场份额稳居第一,从未被超过

以工程师的维度,或者开发者的维度怎么看浏览器,它是一套规范,这套规范能够运行 html、css、javascript 代码,这些内容能够通过超文本传输协定进行传输,通过浏览器的规范进行展示,可能让世界上任何应用浏览器的人都可能看到网页内容

页面的规范有 W3C,语言规范有 ECMAScript,当然还有网络规范,等各种规范由浏览器对立治理。这些规范独特作用,即使是不同的浏览器,它们的规范也是统一的。当然并不是完全一致,一些浏览器还是有本人的想法的,就是这些想法给前端程序员带来了微小的工作量,比方工程师们闻之色变的兼容 IE,次要起因就是 IE 浏览器有本人的想法,搞了很多和他人不太一样的货色,导致同样的代码和逻辑,在 IE 浏览器上展现就有问题

线程和过程

在深入研究浏览器架构之前要把握的另一个概念是过程和线程。通常一个好的应用程序会把本人划分为几个独立的模块和几个相互配合的模块,浏览器自身也是这样,谷歌浏览器为例,它由多个线程和多个过程组成,过程之间相互协作实现浏览器整体的大性能,过程又蕴含很多线程,一个过程内的线程也会相互配合实现这个过程的工作职责

对于前端同学来说,这些概念是含糊的,概念上讲什么过程是资源分配的最小单位,线程是 CPU 调度的最小单位,很多同学听了可能还是有些糊涂,咱们用图来简略讲述一下

以工厂为例,咱们能够把工厂了解成过程,工人了解成线程,工人只能在工厂工作,一个工厂能够有很多工人,同一个工厂内的工人很容易交换,不同的工厂内的工人不容易交换,只是会比拟吃力,一个工厂不会影响另一个工厂,然而工厂内的工人会影响这个工厂的运行

当启动一个应用程序时,会创立一个过程或者多个过程,该程序可能会创立线程来帮忙它工作。操作系统为每个过程提供了可用的内存,所有应用程序状态都保留在内存空间中。当您敞开应用程序时,程序创立的过程也会隐没,占有的内存也会被开释

chrome 架构

理解了过程和线程的关系之后,咱们能够看一下启动 chrome 浏览器须要占用多少过程

多过程架构

谷歌浏览器自带了一个工作管理器,点击浏览器【更多工具】→【工作管理器】

能够看到对应的浏览器过程

各过程作用

这张图其实就表明浏览器其实是多过程的架构,当然这是一直演变的后果,并不是欲速不达的,简略来看一下这些过程的作用

  1. 浏览器过程:次要用来管制浏览器局部,比方地址栏、书签栏、后退和后退的按钮,当然还有一些不可见的局部,比方网络申请和文件的解决,同时也负责其它过程的调度
  2. GPU 过程:跟其它过程关系不大,次要用来独立解决 GPU 工作,比方整个应用程序的绘制,网页的成像性能
  3. 渲染过程:负责网站的渲染,代码运行,web worker 的治理
  4. Network 过程、Storage 过程、Audio 过程等看名字就晓得是用来干嘛的
  5. 百度标签页过程 & 谷歌标签页过程:表明一个标签页就是一个过程
  6. 扩大程序过程:咱们装置的一些插件,每个插件也占用一个过程

多过程优缺点

为什么每个标签或者每个插件都要一个过程呢?其实思考一下不难理解,如果咱们关上的某一个网页无响应了,或者奔溃了,这个时候每个过程是隔离的,并不会影响咱们其它的页面,这种设计其一是保障了标签页的稳定性。同样的如果某个页面死循环不晦涩,其它页面也是感知不到的,内存透露也一样,以后页面因为内存透露敞开这个标签页之后,对应的内存资源就会被回收,变相解决了内存透露导致整个浏览器奔溃的问题

多过程还有一个益处就是安全性,因为操作系统有一种限度过程权限的办法,所以浏览器能够通过某些性能对某些过程采取沙箱解决,比方浏览器限度了渲染过程的任意文件的拜访。沙箱解决隔离了渲染过程,这样即便在渲染过程外面执行恶意程序,恶意程序也无奈冲破沙箱获取零碎的权限

当然并不是多过程都是好的,也有其不足之处,比方我关上了多个百度页面的选项卡,这个时候浏览器调配了多个过程,导致更多的资源占用,尽管这两个内存中的货色完全一致

当然谷歌浏览器针对这一点也做了对应的优化,它的外部限度了能够启动的过程数量,这个限度取决于你的设施的内存和 cpu 的功率,并不是固定死的,设施越好可启动的过程数量就越多。当达到它所限度的数量时,它会优化关上的标签页,比方雷同站点的标签页合并为同一个过程

当然多个标签跟开启多个浏览器相似,谷歌浏览器也在一直优化,将浏览器中的各个局部作为一项服务,从多过程模型到多服务模型,能够轻松的进行过程拆分或者合并。也就是说当你的硬件性能足够,它能够将每个服务拆分到不同的过程,当你的硬件资源无限,它会将这些服务合并到一个过程

站点隔离

后面说咱们每个标签页一个过程,然而这个标签页当中有可能通过 iframe 嵌入了另一个页面,这个时候如果专用一个过程的话可能就会带来危险。所以谷歌浏览器为跨站点的 iframe 页面也开启了一个独自的渲染过程。要思考浏览器同源策略的影响,一个站点无奈在未经容许的状况下拜访其它站点的数据,过程隔离是宰割站点的最无效的形式

站点隔离并不是咱们设想的这么简略,它扭转了 iframe 和页面的交互方式,即使是多个渲染过程,当你关上 devtools 的时候,它们看起来还是那么完满,并且当你用 ctrl+ f 对页面进行搜寻的时候,多个渲染过程之间的搜寻工程师们也优化的你看不出丝毫漏洞

理解了浏览器的架构,来讲一个面试官最喜爱问的面试题,当浏览器输出 url 之后产生了什么?

输出 url 之后产生了什么

咱们应用浏览器的次要目标就是为了搜寻或者拜访某些网站,就让咱们从浏览器的角度,来看看咱们是如何进行搜寻或者网站的拜访的

从浏览器的架构中咱们能够得悉,咱们输出 url 或者搜寻的这一栏是由浏览器过程管制的,其中浏览器过程上面有一些线程,比方管制搜寻栏交互和展现的 UI 线程,当你输出网址或者文字之后,UI 线程便开始了工作

输出还是搜寻

当你在地址栏输出了内容之后,UI 线程要做的操作就是须要进行分别,判断你输出的是 url 还是搜寻的字段,如果是 url,则相应的转到对应的站点。如果是搜寻的字段,则通过浏览器中设置的应用那种搜索引擎,进行对应的站点跳转

不论是搜寻还是站点拜访,最终都会走站点拜访的逻辑,当你在地址栏输出【你好】之后,回车,它也会变成相应的站点 url

如何判断是否是 URL

要判断是否是 URL 就要晓得什么是 URL(Uniform Resource Locator)翻译过去为对立资源定位符,俗称网址

它的规范格局为:

[协定类型]://[服务器地址]:[端口号]/[资源层级 UNIX 文件门路][文件名]?[查问]#[片段 ID]

一个简略的 URL 组成如下所示

一个残缺的 URL 如下所示

只有输出的内容满足这个规定,就认为是一个无效的 URL,间接跳转到对应的网站,否则就执行搜寻逻辑(实质还是跳转到对应的 URL)

获取内容

拿到 URL 之后,是不是立即就会发送申请?其实不是,浏览器还会进行额定的一些查看。

比方安全性查看,查看要拜访的内容在本地是不是有缓存,缓存是否过期?一个较为残缺的前置流程判断大抵如下

这张图其实画的有点多,然而次要是想让大家理解在获取资源之前,其实是有一个缓存的判断的,否则就不会发送对应的申请

在控制台可能看到一些资源尽管返回了 200 的状态码,然而理论是来自缓存,并没有从服务器获取数据,抓包的话也是没有对应的申请的

强缓存和协商缓存

下面其实讲的是强缓存,强缓存是有对应的过期工夫的,工夫是响应标头 expires 管制,当然图中还有标注 cache-control 这个字段,这两个字短有啥区别呢?

expires 是 http1.0 的产物,cache-control 是 1.1 的产物,两个同时存在,cache-control 的优先级更高

图中还有一个字段,是 last-modified,这个次要用于协商缓存,如果强缓存失效,则间接走强缓存,不失效则走协商缓存,协商缓存会在申请头中增加 if-Modified-Since 的字段,这个字段的值就是第一次申请文件的时候返回头中的 last-modified,服务收到申请后,比照服务中文件批改的工夫,如果没变动,状态码返回 304,浏览器间接从缓存中获取文件,如果有更新,则服务端会返回 200 状态,并返回新的文件,最初浏览器再将新的响应报文和对应的缓存标识缓存起来

协商缓存还有一个字段是 etag,图中也有,它是依据文件内容是否有批改来决定是否应用缓存数据

上述的缓存有些内容可能还未波及,然而大家先理解一个大略,像申请报文之类的咱们后续会具体阐明,因为这里波及到缓存,大家有个概念即可,咱们看一下失常的申请流程是什么样的,不走缓存的这种

DNS 解析

如果缓存都未命中,咱们就须要浏览器去发送申请,申请网址对应的资源,然而咱们都晓得,服务器的地址都是一段 ip 地址,然而咱们明明输出的是一个 URL,URL 怎么可能晓得咱们拜访的是哪一个服务器呢?

这就引出了 DNS 的概念,DNS 其实就是用于实现域名和 IP 互相映射的一个分布式数据库,它能够将域名翻译成计算机可辨认的 IP 地址

借用网上的一张图,来看看 DNS 的查问流程是怎么的

  1. 看浏览器中是否有 URL 对应的 IP 缓存
  2. 当浏览器中没有的时候,查看零碎的 hosts 文件是否有域名对应的 IP
  1. 查看路由器缓存(上述两种都没有的时候)这些都是客户端的缓存
  2. 当客户端没有对应缓存的时候,开始询问服务器
  3. 进入 ISP DNS 缓存,也就是运营商缓存,比方你用的挪动或者联通的宽带,他们有本人的 DNS 缓存服务器
  4. 当以上均没有,则进入根域名服务器进行查问,寰球就 13 台根域名服务器,1 个主的,12 个辅的。若无则将其管辖范畴内顶级域名(如.com、.cn 等)服务器 IP 通知本地 DNS 服务器
  5. 询问顶级域名服务器,若无记录则将其管辖范畴内权威域名服务器的 IP 地址通知本地 DNS 服务器
  6. 询问权威域名(主域名)服务器
  7. 本地域名服务器把返回的后果保留到缓存,以备下一次应用,同时将该后果反馈给客户端,客户端拿到了对应的 IP 地址,就能拜访到对应的服务器,DNS 查问完结

DNS 是极其重要的一环,这个环节出了问题就无奈进行后续的操作,它是客户端拜访互联网的关键所在

建设 TCP 连贯

通过 DNS 解析咱们曾经获取到了文件所在的服务器的 IP,有了这个 IP 之后,咱们就须要发送申请获取对应的文件了,然而在获取文件的第一步,首先要做的就是建设 TCP 连贯

一个页面的性能的影响因素首要的就是首屏渲染工夫,而首屏渲染工夫其中的一个影响因素就是网络加载速度,决定性的内容就是文件的返回工夫

如果你对网络有充沛的理解,那必定晓得网络的根底是网络协议,网站则是基于 http 协定,http 又是基于 TCP/IP 的。计算机底层是 101010 这种二进制数据,文件传输也是二进制数据,那这些数据是如何到咱们的浏览器的?第一步就是要建设服务器与客户端之间的连贯

下面咱们曾经获取到了服务端的 IP 地址,每个计算机都有一个独立的 IP 地址,客户端和服务端有了各自的地址之后就可能精准传输了,整个过程如下图所示

建设连贯

建设连贯的过程就是三次握手的过程,示意整个过程要发送三次包

  1. 客户端对服务端发动连贯申请,携带 syn=1、seq= x 的报文
  2. 服务端接管到了客户端的申请,如果确认能够连贯,则返回报文,携带 syn=1、ack=x+1、seq=y
  3. 客户端收到服务端发送的报文之后,查看是否符合要求,告诉利用连贯曾经建设,向服务端发送确认信息 seq=z,ack=y+1,服务端校验正确则建设胜利

连贯建设胜利,就能够传输数据了

数据传输实现,有一个断开的过程

连贯断开

断开连接被称为四次挥手过程,示意整个断开过程要发送四次包,须要双向连贯和双向敞开,不论是客户端还是服务端,任何一方都能够发动断开的过程

  1. 首先被动方要求断开连接,发送 fin=1 ack=z seq= x 的报文,申请断开连接
  2. 被动方接管到报文,发送 ack=x+1、seq= z 表明我晓得了
  3. 发送完数据后,再发送一个 fin=1、ack=x、seq= y 的报文,通知被动方数据发送结束
  4. 被动方收到之后,发送 ack 表明确认收到,tcp 连贯断开

    获取资源

    上一步曾经建设好了连贯,那就开始传输数据了。在该阶段,客户端须要对数据包进行确认操作,在接管到数据包之后,须要发送确认数据包给发送端。所以发送了一个数据包之后,在规定工夫内没有接管到客户端端反馈的确认音讯,则判断为数据包失落,并触发发送端的重发机制。同样,一个大的文件在传输过程中会被拆分成很多小的数据包,这些数据包达到接收端后,接收端会依照 TCP 头中的序号为其排序,从而保障组成残缺的数据。

    申请头

    通过 TCP 以及 UDP 独特作用,这个时候浏览器的网络线程是可能收到服务器的残缺数据,在获取数据的时候,咱们会增加一系列的申请头,比方咱们必须指定申请办法到底是 GET 还是 POST,或者是其它,之前咱们也提到了 If-Modified-Since,用于缓存,还有 Cookie、User-Agent、Referer 等头信息

用这些申请头数据去通知服务器咱们以后须要什么内容,以及通知服务端客户端的一些信息

响应头

当服务端批准或者回绝给客户端返回内容之后,客户端都会收到一些反馈,反馈的内容除了失常咱们想要的数据以外,还有 response 头信息。能够看到对应的状态代码为 200,示意胜利,阐明获取到了服务端返回的对应信息。

这个域名对应的 IP+ 端口就是图中的近程地址,除了状态为 200 还有其它的一些状态,比方 301 告知服务器正在重定向,而后网络线程发动另一个 URL 申请,针对 http 状态码各个阶段的含意大家能够自行理解一下

  1. 1XX Informational(申请正在解决)
  2. 2XX Success(申请胜利)
  3. 3XX Redirection(重定向)须要进行附加操作以实现申请
  4. 4XX Client Error(客户端谬误)
  5. 5XX Server Error(服务器谬误)

下面的图中能够看到响应头外面蕴含了一些信息或者执行了一些操作,比方 Set-Cookie 响应头能够往浏览器外面设置一些 cookie,Conetnt-Type、Cache-Control 等相干

网络线程在查看了头部的这些字节之后,因为传输过程有可能会出现异常,比方失落或者谬误,所以在这里会实现 MIME 类型嗅探(也就是查看一个字节流的内容,试图推断其中文件的数据格式)

下面咱们在拜访百度的时候,Conetnt-Type 为 html 类型的文件,浏览器检测到如果文件类型是 html 的话,之后就会把数据交给渲染过程。当然这里不仅仅只有 html 文件类型,如果是其它文件类型,比方 zip 或者其它的内容,则浏览器会交给下载管理器进行对应资源的下载

这个时候通常也会进行浏览器的安全检查,两方面查看

  1. 如果这个站点和已知的歹意站点匹配,则网络线程收回正告,表明这是一个歹意站点
  1. 还有一个查看的点大家都比拟相熟,那就是跨域问题的检测,跨域实质是浏览器的安全检查机制,如果发现申请的 URL 的协定域名端口任意一个和以后站点不同即为跨域,这个查看也会在这个阶段,确保敏感的跨站点的数据不会进入渲染过程

所以咱们要明确的一点是,跨域是浏览器的安全策略,是浏览器拦挡的,如果你用抓包工具的话,会发现数据其实曾经给到咱们了,当然 post 申请还会存在一个预检的过程,避免抓到数据,在发正式申请之前,预检服务端是否做了跨域的解决

渲染

以后曾经筹备好了对应的数据,也就是 html 文件。并且实现了前置的所有信息查看,那么网络线程就会通知 UI 线程数据曾经准备就绪,UI 线程要做的就是找一个渲染过程用于 html 的渲染

然而这个过程是有优化的空间的,因为网络线程申请数据的过程是须要工夫的,所以在网络线程发送 URL 的申请的时候,它曾经晓得以后是要拜访哪个站点,UI 线程将会并行查找并启动渲染过程,这个时候申请到数据的时候,渲染过程曾经是待命的状态,可用于间接渲染

这个时候须要浏览器过程跟渲染过程通过 IPC 进行通信,通信过程还须要传递数据流,不便渲染过程能够继续接管 html 数据,一旦渲染过程渲染结束,便会告诉浏览器过程以后结束,导航阶段就实现了,就开始了加载文档的过程

这个时候,地址栏更新。平安指示器和站点设置的 UI 反馈站点的信息,选项卡的历史记录会被更新,后退后退等历史记录逐渐被更新,历史记录同样也会在磁盘上存储一份,不便进行整个历史浏览的检索

咱们晓得,当页面进行加载的时候,浏览器 UI 上 tab 标签页上会有一个加载中的 loading 标记,一旦渲染过程实现渲染,渲染过程会将回调通过 IPC 发送到浏览器过程(onload 事件实现的时候,蕴含所有子页面(frame)),浏览器 UI 上 loading 标记隐没,显示实现状态,然而这个完结并不代表页面渲染就实现了,有可能还有 JavaScript 在加载额定的资源或者新的视图

这个时候渲染过程便开始渲染,具体是如何渲染的咱们之后具体讲述,咱们再看一下在这根底如何拜访另一个页面

拜访不同站点

在以后标签页,咱们进行另一个页面拜访的时候,浏览器过程会反复下面的过程。然而开始的时候,浏览器会确认以后的站点是否关怀 beforeunload 这个事件,如果对这个事件做了监听,当拜访另一个网站或者刷新的时候,就会弹出一下选项进行确认

window.addEventListener('beforeunload', (event) => {
  // 显示确认对话框
  event.preventDefault();
  // 为了兼容解决,Chrome 须要设置 returnValue
  event.returnValue = '';
});

当然,这只是泛滥生命周期中的一个节点,还有比方 unload,pagehide,pageshow 等等,感兴趣的能够参照 https://developers.google.com/web/updates/2018/07/page-lifecycle-api

service worker

这里咱们再插入一个知识点,service worker,它的作用是什么呢?其实就是服务器和浏览器之间的一个中间人。目标是为了拦挡网站的所有申请,能够进行相应的判断,如果一些接口能够间接应用缓存就间接返回缓存

service worker 独立于以后网页的线程,所以执行大量的操作也不会阻塞主线程

渲染过程如何工作

下面的过程把 html 文件曾经交给了渲染过程,渲染过程负责页签的显示,在一个渲染过程中,主线程负责解析,编译代码,运行等工作,它的外围就是将 HTML、CSS 和 JavaScript 转换成用户能够与之交互的网页

当然渲染过程是一个多线程架构,它次要有以下线程:GUI 线程、JavaScript 引擎线程、定时器触发线程、事件触发线程、http 申请线程、合成线程和 IO 线程

GUI 渲染线程

拿到数据之后,GUI 渲染线程就开始解析 HTML 并将其转换成 DOM(文档对象模型),DOM 是浏览器对页面的外部示意,javascript 获取和操作的页面元素实质是浏览器提供的 DOM 数据,同时当页面产生重绘和回流的时候,该线程也会执行

在解析过程中,即使是你的 html 语法有一些异样,比方没有敞开标签,匹配谬误等,浏览器也不会抛出异样,比方如下代码,在浏览器上会主动解析胜利

<body>
  <div>
  </p>
</body>

这里还须要留神一点的是,GUI 渲染线程和 JavaScript 引擎线程是互斥的,当 JavaScript 引擎线程执行的时候,GUI 线程是被挂起的,相当于是解冻状态,GUI 的更新会被保留在一个队列中等 JavaScript 引擎闲暇的时候立即执行。之所以这样是因为 JS 代码可能会扭转 DOM 构造,所以 JavaScript 引擎执行工夫过长是会阻塞页面的渲染的,理解这一点也就晓得为什么 fiber 架构为什么可能让大型利用看起来不卡顿

在解析 html 的过程中,其实还有一些其它的资源,比方 img 或者 link,这个时候就会给浏览器过程的网络线程发送信息,GUI 线程会依据这些额定的资源是否会阻塞转换过程而决定是不是须要资源加载结束。比方碰到 script 标签有可能就会阻塞,然而也有例外,script 标签增加了 async 或者 defer 属性

JavaScript 引擎线程

负责解析 JavaScript 脚本,运行代码

事件触发线程

比方咱们的点击事件,滚动事件,异步申请,或者执行 setTimeout 等这些事件时,会将对应的工作增加到事件触发线程,当这个事件被触发的时候,则把触发的事件回调增加到待处理队列的队尾。因为 javascript 是单线程的,所以解决这些事件都必须排队

定时器线程

setInterval 与 setTimeout 所在线程,计数通过下面的内容,能够失去不可能通过 javascript 线程计数,否则会阻塞,因而会有一个独自的线程进行计数的解决,等待时间达到后,将回调函数增加到事件队列

HTTP 申请线程

在 XMLHttpRequest 连贯后是通过浏览器新开一个线程申请,监测到获取了对应的内容后,将回调函数增加到事件队列,再由 javascript 引擎执行

合成线程前面会讲,IO 线程次要用于和其它的线程通信

布局

以后 DOM 曾经有了,然而精美的页面光有 DOM 是不够的,只有 DOM 是不会呈现咱们的五彩斑斓的页面,须要 CSS 让页面变得更好看,GUI 线程会解析 CSS 并决定每个 DOM 元素的款式

如果你没有设置对应的款式,浏览器也有本人的内置的一些标签款式,比方 h1-h6

有了款式,渲染过程曾经晓得了每个结点出现的成果,然而节点的地位信息怎么来,这个时候须要布局树,渲染过程会遍历 DOM 构造(蕴含款式),布局树只蕴含在页面中显示的元素,当一个元素被设置为 display: none 的时候布局树中是没有这个元素的。同理如果 div::before {content: ‘Hi!’},则布局树中是存在这个 Hi 的,DOM 树 javascript 可能获取,然而布局树获取不到

布局树的形容十分具备挑战性,因为你须要对整个页面进行准确的描述。布局中存在浮动、定位、固定、文字换行,主动伸缩,各种元素的联合,能够设想这个工作如许沉重

绘制

咱们有了布局树的信息之后是不是就能绘制了,其实并不是,尽管你晓得了每个元素的地位,然而它们绘制的程序是怎么的其实还是不分明,到底哪个元素先,哪个元素后,理解 PS 的同学,必定晓得图层的概念,哪个元素应该在哪个元素的顶部?CSS 有管制元素层级的一个属性,叫做 z -index,用过的同学应该都理解

这个阶段会通过布局树造成绘制记录,绘制记录实质就是绘制的一系列步骤,比方我要先干什么,在干什么(先绘制背景,再绘制元素内容,再绘制形态等等)

渲染

渲染的过程开销是很大的,任何一个小的变动都会引起一系列的变动,当布局树发生变化的时候,绘制须要从新构建页面变动,页面有动画的成果的时候,每一帧都须要更新动画内容,如果无奈保障帧动画,给用户感官上就会呈现卡顿

javascript 也会阻塞页面的渲染,导致卡顿的产生,能够将 Javascript 操作优化成小块,而后应用 requestAnimationFrame()

  • 重排:示意布局树发生变化的时候,整个布局树要从新构建,造成绘制记录,从新回炉修炼
  • 重绘:不影响元素地位信息的,比方元素的色彩发生变化,然而元素地位未产生扭转,只须要重绘

    合成

    目前咱们曾经有了所有的信息,文档构造 - 元素款式 - 元素几何 - 布局树 - 绘制记录,最终将绘制记录转换到屏幕上的像素称之为光栅化

之前的形式是可视区域进行光栅化,滚动的时候再次进行光栅化,如下所示

然而当初浏览器有着更好的解决形式,这个形式被叫做合成

合成会将一个页面拆成很多层,每个层在不同的的合成线程中进行光栅化,而后组合成一个新的页面。滚动过程中如果这个层曾经光栅化,则应用曾经光栅化的层进行合成

那这个时候问题就来了,一个层中要蕴含哪些元素呢?主线程须要遍历布局树,做为开发者,想要创立一个新的层,能够应用 css 属性 will-change 让浏览器创立层

光栅化各个层之后,将其存储在 GPU 的缓存中,合成线程也可能决定相应的优先级,保障用户看到的局部最先被光栅化,每当有交互发生变化,合成线程就会创立更多的合成帧而后通过 GPU 将新的局部渲染进去

浏览器的事件体系

事件是什么?比方按钮的点击,input 输入框的内容输出,鼠标滚轮和拖拽,都是事件

交互的时候浏览器过程最先接管到事件,浏览器关怀的只有以后事件产生在哪个页签,而后将事件地位信息和事件类型发送到当前页签的渲染过程,渲染过程会找到事件产生的元素和对应的事件

然而后面也说到,页面是被光栅化的,在合成线程解决页面的时候,合成线程会标记有事件监听的区域,有这些信息,合成线程就会将触发的事件发送给主线程解决

总结

浏览器的多过程架构,依据不同的性能划分了不同的过程,过程内不同的使命划分了不同的线程,当用户开始浏览网页时候,浏览器过程进行解决输出、开始导航申请数据、申请响应数据,查找新建渲染过程,提交导航,之后渲染又进行了解析 HTML 构建 DOM、构建过程加载子资源、下载并执行 JS 代码、款式计算、布局、绘制、合成,一步一步的构建出一个可交互的 WEB 页面,浏览器过程又承受页面的交互事件信息,并将其交给渲染过程,渲染过程内主过程进行命中测试,查找指标元素并执行绑定的事件,实现页面的交互。

刚踏上开发之路时,我简直只关注怎么去写代码、怎么晋升本人的生产效率。诚然,这些事件很重要,但与此同时咱们也该当思考浏览器会怎么去解决咱们书写的代码。古代浏览器始终致力摸索如何提供更好的用户体验。书写对浏览器敌对的代码,反过来也能提供敌对的用户体验。心愿可能通过这节课让大家理解浏览器的运行机制和原理,构建出对浏览器更为敌对的代码。同时也可能一直优化咱们的业务,让用户体验更上一层楼,这就是本节课全部内容

最初,感激大家浏览,码字不易,一键三连

退出移动版