在浏览器地址栏里输入内容时,浏览器内部会发生什么事?

当在地址栏输入地址时,浏览器进程的UI线程会捕捉输入内容:

如果输入的是网址,则UI线程会启动一个网络线程来请求DNS进行域名解析,接着开始链接服务器获取数据;

如果输入的不是网址,是一串关键词,浏览器就知道是需要搜索,于是就会用默认配置的搜索引擎来查询

网络线程获取到数据后会发生什么样的事?

        网络线程获取到数据后,会通过safeBrowsing来检查站点是否是恶意站点,如果是,则会提示个警告页面,告诉这个站点有安全问题,浏览器会阻止访问,当然也可以强行继续访问

        safeBrowsing是谷歌内部的一套站点安全系统,通过检查该站点的数据来判断是否安全,比如通过查看该站点的IP是否在谷歌的黑名单之内,当返回数据准备完毕,并且安全校验通过时,网络线程会通知UI线程,我这就准备好了,该你了。

        然后UI线程会创建一个渲染器进程(Renderer Thread)来渲染页面,浏览器进程通过IPC管道将数据传递给渲染器进程,正式进入渲染流程

        渲染器进程接到的数据也就是HTML,渲染器进程的核心任务就是把html,css,js,image等资源渲染成用户可以交互的web页面。 渲染器进程的主线程将html进行解析,构造DOM数据结构,DOM也就是文档对象模型,是浏览器对页面在其内部的表示形式,是web开发程序员可以通过JS与之交互的数据结构和API。

        HTML首先经过tokeniser标记化,通过词法分析将输入的html内容解析成多个标记,根据识别后的标记进行DOM数构造,在DOM树构造过程中会创建document对象,然后以document的为根节点的DOM树,不断进行修改。向其中添加各种元素,html代码中往往会引入一些额外的资源,比如图片,CSS,JS脚本等,图片和CSS这些资源许需要通过网络下载或者从缓存中直接加载,这些资源是不会阻塞HTML的解析,因为他们不会影响DOM的生成,但当HTML标解析过程中遇到script标签,就会停止html解析流程,转而去加载解析并且执行JS。

为什么不会直接跳过JS加载和执行,而继续执行HTML解析,等html解析完后再加载运行JS呢?

        这是因为浏览器并不知道JS执行是否会改变当前页面的HTML结构,如果JS代码里面用了document.write方法来修改html,之前的html解析就没有任何意义了,这也就为什么我们一直说要把script标签要放在合适的位置,或者使用async或defer属性来异步加载执行JS。

        在HTML解析完之后,会获得一个DOM Tree(树),但我们还不知道DOM树上的每个节点,主线程需要解析CSS,并确定每一个DOM节点的计算样式,即使没有提供自定义的CSS样式,浏览器也会有自己的默认样式表,比如H2的字体要比H3的大。

        在知道DOM结构和每一个节点的样式后,接下来需要知道每个节点需要放在页面上的哪个位置,也就是节点的坐标以及该节点需要占用多大的区域,这个阶段被成为Layout布局,主线程通过遍历dom和计算好的样式来生成Layout Tree,layout tree上的每个节点都记录了X,Y坐标和边框尺寸,需要注意的一点是DOM Tree和Layout Tree并不是一一对应的,设置了display:none的节点不会出现在Layout Tree上,而在before伪类中添加了content直的元素,content里的内容会出现在Layout上,不会出现在DOM树里面,这是因为DOM是通过HTML解析获得并不关系样式,而Layout Tree是根据DOM和计算好的样式来生成的,Layout Tree是和最后展示在屏幕上节点是对应的。

        这时候还需要知道以什么样的顺序绘制(paint)的,举例来说:z-index属性会影响节点绘制的层级关系,如果我们按照dom的层级结构来绘制页面,则会导致错误的渲染。为了在屏幕上保证正确的层级关系,主线程遍历Layout tree创建的一个绘制记录表(Paint Record),该表记录了绘制的顺序,这个阶段被称为绘制(paint)。

        现在知道了文档的绘制顺序,该把这些信息转化成像素点了,显示在屏幕上了,这种行为被成为栅格化(Rastering),Chrome最早使用了一种很简单的方式,只栅格化用户可视区域(Viewport)的内容,当用户滚动页面时,再栅格化更多的内容来填充缺失的部分,这种方式带来的问题显而易见,会导致展示延迟,随着不断优化升级,现在的Chrome使用了一种更为复杂的栅格化流程,叫做合成(Composting),合成是一种将页面的各个部分成多个图层,分别对其进行栅格化,并再合成器线程(Composting Thread)中,单独进行合成页面的技术。简单来说就是页面所有的元素,按照某种规则进行分图层,并把图层都栅格化好,然后只需要把可视区的内容组合成一帧,展示给用户即可。

        主线程遍历Layout Tree 生成 Layer(图层)Tree,当 Layer Tree生成完毕和绘制顺序确定后,主线程将这些信息传递给合成器线程,合成器线程将每个图层栅格化,由于一层可能像页面的整个长度一样大,因此合成器线程将他们切分成许多图块,然后将每个图块发送给栅格化线程,栅格化线程栅格每个图块,并将它们存储在GPU内存中,当图块栅格画完后,合成器线程将收集成为“draw quads”的图块信息,这些信息记录了图块在内存中的位置,和在页面的哪个位置绘制图块的信息,根据这些信息合成器线程生成了一个合成器帧(Compostior Thread),然后合成器Frame(帧)通过IPC传送给浏览器进程,接着浏览器进程将合成器帧传送到GPU,然后GPU渲染展示到屏幕上,这是就可以看到页面内容了

       当页面发生变化,比如滚动页面,都会生成一个新的合成器帧,新的帧再传给GPU,然后再次渲染到屏幕上。

总结一下:

浏览器进程中的网络线程请求获取到html数据后,通过IPC管道将数据传递给渲染器进程的主线程,主线程将html解析构成DOM树,然后进行样式计算,根据DOM树和生成好的样式生成Layout Tree,通过遍历Layout Tree生成绘制顺序表(paint),接着遍历Layout Tree生成Layer Tree。然后主线程将Layer Tree和绘制顺序表一起传给合成器线程,合成器线程按规则进行分图层,并把图层分为更小的图块(tiles)传给栅格化线程进行栅格化,栅格化完成后,合成器线程会获得栅格线程传过来的“draw quads”图块信息,根据这些信息合成器线上合成了一个合成器帧,然后将合成器帧通过IPC传回给浏览器进程,浏览器进程再传到给GPU进行渲染,之后就能看到页面展示在屏幕上了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值