Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

浏览器是怎么渲染一个页面的 #10

Closed
sevenCon opened this issue Aug 14, 2019 · 0 comments
Closed

浏览器是怎么渲染一个页面的 #10

sevenCon opened this issue Aug 14, 2019 · 0 comments
Labels
浏览器 browser render details

Comments

@sevenCon
Copy link
Owner

前言

今天, 来探讨一下, 浏览器是怎么工作, 包括获取文档, 到渲染html到界面上.

浏览器从一个url, 获取到显示到界面上,经过 DNS查找,缓存策略, 发送请求的三次握手, 接收数据包, 解析, 再请求link的css, 开始构建DOM tree, 同时还开始构建Css tree, 2课合并生成位图, 合并位图, 把内存中的位图数据发送给GPU/驱动.

1.DNS查找的过程

  1. 输入的域名解析首先会在本地的hosts文件里面查找,如果有对应的映射,就返回这个隐射关系,结束解析
  2. 如果hosts文件没有对应的隐射关系, 那么会在本地的DNS缓存记录里面查询, 是否有缓存记录, 如果有就返回改缓存记录, 结束解析
  3. 如果本地的DNS缓存记录也没有, 那么会把解析发送到TCP/IP配置中的DNS服务器, 这个服务器也叫本地DNS服务器. DNS开始查找这个域名隐射. 解析失败会跟着在缓存记录里面查找.
  4. 如果本地DNS服务器解析失败, 那么会分2种情况, 开启转发模式, 关闭转发模式.
  5. 如果没有开启转发模式的, 那么最终的请求会发送根域名服务器, 进行查询该域名解析下的.com, .cn, .gov,...等等顶级域名服务器的IP地址, 再由本地DNS服务器发送请求去顶级域名服务器, 如果顶级域名不能解析,则会返回一个二级的DNS服务器的IP地址, 本地DNS服务器则再去下一级的DNS服务器的地址查找, 这样不断的递归这个查找过程, 直到查找到目标的解析地址. 拿到目标的解析地址, 本地的DNS服务器会进行缓存一份.
  6. 如果本地DNS服务器开启了转发模式, 那么就会把当前这个不能解析的域名发送给上一级DNS服务器, 如果上一级DNS服务器的DNS服务器不能解析失败, 那么再向上转发这个域名, 直到返回目标的IP地址.

image

解析html文档

1. 解析html的文档, 生成dom tree.

2. 接着, 解析css的样式, 生成css tree.

这个流程没有什么问题.

乍一看这个流程好像很清晰,但是仔细的一想, 这过程太模糊了, 主要看几个主要的资源.

  • 首先html的渲染, 会被script阻塞吗?
  • link标签的css是同步下载, 还是异步下载,下载完的解析会阻塞html的解析吗?
  • link加载的css, 会阻塞js的运行吗?

注意的是link里面的css,的下载是并行的, 下载和解析都不会影响html的解析, 但是html的渲染, 也就是attachment 合成渲染树这一步, 需要等待所有的css加载完毕, 再去渲染.

  • script 脚本的运行, 因为script中不可避免的会获取和操作css rules的情况, 所有必须等待所有的css下载并解析完成, 默认情况下, script是同步下, 同步解析的,但是defer,async有了之后情况会不一样.
  • css的下载解析和成CSS Tree 的过程阻塞影响html的解析, 但是html的渲染会等待css tree, 也就是户阻塞html的渲染

image

总结:

  • 解析和渲染, 是2个阶段, 解析阶段会进行css下载解析, 解析阶段css和dom互不影响.
  • 而script加载解析运行, css的加载解析会阻塞script的运行, 如果script的后面还有标签, 那么会阻塞html的解析, 这也是常把script放最后的原因.
  • 而在script阶段, 浏览还会另起一个线程, 去对后面的dom涉及到的资源进行分析加载. 这个过程叫预解析.

渲染树,重排和重绘

渲染树的每一个节点直接关联一个盒模型, display:none的节点不会被构建. 和dom树不是一对一对应的, 因为一个dom的节点可能出现多个盒子的情况, 比如inline的元素的折行会产生多个行框盒子. 在渲染树上也就是多个渲染对象了.此外一些替换元素的渲染也会被特殊处理. 比如select框的渲染就不能简单用一个盒模型来渲染.

重排

当渲染对象被创建并添加到树中,它们并没有位置和大小,计算这些值的过程称为layout或reflow, 或者叫重排, 而html的渲染遵循一般流模型进行渲染页面, 不存在后面的元素影响前面的元素的大小的情况.这个计算宽高的过程分为2种情况, 分别是

  • 全局的layout, 这种情况户发生在比如根元素的字体大小变化, 导致后代的布局发生变化.所以这个时候需要整个渲染树都需要重新layout.
  • 增量的layout, 比如某一个标签的大小宽度等属性发生变化, 只需要调用当前元素及其后代元素的layout方法, 重新去生成大小宽高信息.

webkit 引入了 Dirty bit系统进行控制每个一节点是否需要重新layout的情况. 标记为dirty的情况, 和children are dirty的情况,

  • dirty意味着当前的节点及其子元素需要layout,
  • 而children are dirty意思是当前这个节点可能不需要layout
    但是子节点至少需要一次layout.

一个可以的优化的地方, 是如果只是位置的变化,大小没有变化 那么会重新去缓存里面去获取该节点及其子节点的layout的信息, 而不必重新生成.

layout的过程

首先, layout是一个不断递归的过程, 在这个过程中, parent节点的大小依赖于child的layout,parent的高度, 取决于child的排列高度叠加.

  • parent 高度

    • parent决定首个子元素的layout的x, y坐标
    • 调用节点的layout方法, 更新节点的高度
    • parent节点使用子节点的高度, 盒模型的margin, paddin等设置自己的高度
    • layout的过程结束, 设置dirty:false;
  • parent 宽度

    • parent 的宽度的过程
    • 容器的宽度是取0和计算宽度的最大值, 理论上contentWidth = clientWidth() - paddingLeft - paddigRight, clientW idth和clientHeight代 表一个对象内部的不包括border和滑动条的大小
    • 元素的宽度指的是样式中定义的宽度, 这个宽度可以通过设置百分比, 获得父类相对大小.
    • 加上border, padding值.

以上即是最佳的宽度计算.也就是设置节点的宽度的过程, 概括来说包含了内容区域的宽度、自身的padding、border、 、定义的样式宽度总和.

  • line breaking
    line breaking发生在内联元素上, 文字折行显示的时候,会存在多个行框盒子.

这个时候其实是通过和父元素的通信的, 也就是说, 父元素在遇到内联元素着汗显示的情况下, 需要另外建立一个内联盒子, 渲染折行的部分.

绘制

绘制过程用到的方法是paint

绘制的内容和重排一样, 分为全局绘制增量绘制, 全局绘制通常需要同时会子节点的内容, 增量绘制, 只改变一部分的渲染对象, 通过让这部分的设置为dirty区域,产生一个paint区域.

绘制的顺序:

  1. 背景色
  2. 背景图
  3. border
  4. children
  5. outline

这些顺序在文档中有明确的定义说明:https://www.w3.org/TR/CSS21/zindex.html

绘制前, 会把渲染对象对应的矩形区域进行生成位图,理论上一个盒子对应一张位图,生成位图的一个优化是合成有效部分,缩小位图可以有效的减少内存占用和渲染压力.

常见的一些css会排斥位图合并, 比如position属性部位static的, 带动画效果的等等.

总结

  • 浏览器DNS的查找过程
  • 拉取解析dom, css的过程, 这里省略了语法分词, 句法分词, 这里棣属于编译原理, 可惜自己是在没有心思阅读枯燥而不理解的内容.
  • 了解了重排,重绘的操作细节. 这个细节可能没有那么的细, 但后面有时间会一步一步了解, 并补充进来.

浏览器的渲染过程,相关的知识其实很少, 网络上并详细却官方的资料查阅, 一个有效的了解的途径是阅读chromium的邮件组细节, 和项目的开源代码, 但一个英语不好, 一个开源代码太官方, 又是c的, 可能还是因为懒吧, 哈哈哈.

参考

How Browsers Work: Behind the Scenes of Modern Web Browsers
https://kb.cnblogs.com/page/129756/#chapter5
https://juejin.im/post/5b88ddca6fb9a019c7717096
https://blog.51cto.com/369369/812889

本文的github地址浏览器是怎么渲染一个页面的, 如果有版权或其他问题, 请issue留言.感谢

@sevenCon sevenCon added the 浏览器 browser render details label Aug 14, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
浏览器 browser render details
Projects
None yet
Development

No branches or pull requests

1 participant