html,css,javascript是怎样变成页面的


Posted in HTML / CSS onMay 07, 2023

浏览器是多进程的,有浏览器主进程,网络进程,渲染进程,插件进程等,在将html,css,javascript解析成一个页面的时候,就需要多个进程的分工合作。
当浏览器接受到一个请求的响应数据,并且该数据的类型(content-type)为text/html,浏览器就会知道这是一个html页面,于是网络进程就会将收到的数据交给渲染进程后,就进入了渲染阶段,也就是在这一个阶段,浏览器会根据数据生成一个新的页面。

在执行的过程中,会被分为很多个子阶段,输入的HTML经过这些子阶段,最后会输出像素,这个处理过程叫做渲染流水线,按照渲染的时间顺序,流水线可分为如下几个子阶段:

构建DOM树

网络进程交给渲染进程的字节流渲染进程是无法识别的,需要先转化为dom树,在渲染进程内部有个html解析器,就负责将字节流转化为dom树

解析器的工作过程

  • 通过分词器将字节流转化为token,token分为tag token 和文本token,tag token又分为 starttag token 和 endtag token,比如
    就是一个starttag token,
    就是一个endtag token
  • 接着需要将token解析为dom节点,并将dom节点添加到dom树中,这两个过程是同步进行的
  • html解析器维护了一个token的栈结构,该token栈主要用来计算节点之间的父子关系,在第一个阶段中生成的token会被按照顺序压入这个栈中,具体规则如下:
    • 如果压入到栈中的是StartTag Token,HTML 解析器会为该 Token 创建一个 DOM 节点,然后将该节点加入到 DOM 树中,它的父节点 就是栈中相邻的那个元素生成的节点。
    • 如果分词器解析出来是文本 Token,那么会生成一个文本节点,然后将该节点加入到 DOM 树中,文本 Token 是不需要压入到栈中,它的父节点就是当前栈顶 Token 所对应的 DOM 节点。
    • 如果分词器解析出来的是EndTag 标签,比如是 EndTag div,HTML 解析器会查看 Token 栈顶的元素是否是 StarTag div,如果是,就将 StartTag div 从栈中弹出,表示该 div 元素解析完成。

样式计算

样式计算的目的是为了计算dom节点中每个元素的具体样式,具体可以分为以下三个步骤:

  • 把css转化为浏览器能够理解的结构,可以用document.styleSheet查看

  • 转换样式表中的属性值,使其标准化

    属性标准化就是将一些属性值转化为渲染引擎容易理解的,标准化的计算值,比如一些字体的单位是em的就需要转化为px

  • 算出dom树中每个节点的具体样式

    样式计算的第一个规则就是css继承,第二个是层叠

    总之,样式计算阶段的目的是为了计算出DOM节点中每个元素的具体样式,在计算过程中需要遵守CSS的继承和层叠两个规则。这个阶段最终输出的内容是每个DOM节点的样式,并被保存在ComputedStyle的结构内。

布局阶段

布局也就是计算出dom树中可见元素的几何位置,chrome在布局阶段需要完成两个任务:创建布局树和布局计算

  • 创建布局树

    在DOM树一般还会含有很多不可见的元素,比如head标签,还有使用了display:none属性的元素。所以在显示之前,我们还要额外地构建一棵只包含可见元素布局树。

    为了构建布局树,浏览器大体上完成了下面这些工作

    • 遍历DOM树中的所有可见节点,并把这些节点加到布局中;
    • 而不可见的节点会被布局树忽略掉,如head标签下面的全部内容,再比如body.p.span这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树
  • 布局计算

    现在我们有了一棵完整的布局树。那么接下来,就要计算布局树节点的坐标位置了

分层

接下来,渲染引擎需要为特定的节点生成专用的图层,并生成一颗对应的图层树。图层叠加起来就是最终的页面图像

通常情况下,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。如上图中的span标签没有专属图层,那么它们就从属于它们的父节点图层。但不管怎样,最终每一个节点都会直接或者间接地从属于一个层。

满足以下条件才会被提升为单独的层

  • 第一点,拥有层叠上下文的元素会被提升为单独的一层

    页面是个二维平面,但是层叠上下文能够让HTML元素具有三维概念,这些HTML元素按照自身属性的优先级分布在垂直于这个二维平面的z轴上。你可以结合下图来直观感受下:

  • 需要剪裁的地方也会被创建为图层

    标签里面的内容超出了标签的宽度和高度,就会出现剪裁。出现这种裁剪情况的时候,渲染引擎会为文字部分单独创建一个层,如果出现滚动条,滚动条也会被提升为单独的层

图层绘制

在完成图层树的构建之后,渲染引擎会对图层树中的每一个图层进行绘制

绘制的过程就是把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表

绘制列表中的指令其实非常简单,就是让其执行一个简单的绘制操作,比如绘制粉色矩形或者黑色的线等。而绘制一个元素通常需要好几条绘制指令,因为每个元素的背景、前景、边框都需要单独的指令去绘制。所以在图层绘制阶段,输出的内容就是这些待绘制列表。

栅格化操作

绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。

当图层的绘制列表准备好之后,主线程会把该绘制列表提交(commit)给合成线程

  • 通常一个页面可能很大,但是用户只能看到其中的一部分,我们把用户可以看到的这个部分叫做视口(viewport)。

在有些情况下,有的图层可以很大,比如有的页面你使用滚动条要滚动好久才能滚动到底部,但是通过视口,用户只能看到页面的很小一部分,所以在这种情况下,要绘制出所有图层内容的话,就会产生太大的开销,而且也没有必要。

基于这个原因,合成线程会将图层划分为图块(tile),这些图块的大小通常是256x256或者512x512

合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。而图块是栅格化执行的最小单位。渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的,运行方式如下图所示:

通常,栅格化过程都会使用GPU来加速使用,使用GPU生成位图的过程叫做快速栅格化,或者GPU栅格化,生成的位图会保存在GPU内存中

合成和显示

一旦所有的图块都被栅格化,合成线程就会生成一个绘制图块的命令—‘DrawQuad",然后将该命令提交给浏览器进程

浏览器进程里面有一个叫viz的组件,用来接收合成线程发过来的DrawQuad命令,然后根据DrawQuad命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。

到这里,经过这一系列的阶段,编写好的HTML、CSS、JavaScript等文件,经过浏览器就会显示出漂亮的页面了。

总结

一个完整的渲染流程大致可总结为如下

  • 渲染进程将HTML内容转换为能够读懂的DOM树结构。
  • 渲染引擎将CSS样式表转化为浏览器可以理解的styleSheets,计算出DOM节点的样式。
  • 创建布局树,并计算元素的布局信息。
  • 对布局树进行分层,并生成分层树。
  • 为每个图层生成绘制列表,并将其提交到合成线程。
  • 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  • 合成线程发送绘制图块命令DrawQuad给浏览器进程。
  • 浏览器进程根据DrawQuad消息生成页面,并显示到显示器上
 
HTML / CSS 相关文章推荐
纯CSS3绘制打火机动画火焰效果
Jul 18 HTML / CSS
CSS3实现淘宝留白的方法
Jun 05 HTML / CSS
如何使用localstorage代替cookie实现跨域共享数据问题
Apr 18 HTML / CSS
HTML5 Canvas概述
Aug 26 HTML / CSS
只要五步 就可以用HTML5/CSS3快速制作便签贴特效(图)
Jun 04 HTML / CSS
HTML5 window/iframe跨域传递消息 API介绍
Aug 26 HTML / CSS
HTML5通用接口详解
Jun 12 HTML / CSS
详解android与HTML混合开发总结
Jun 06 HTML / CSS
Html5 webview元素定位工具的实现
Aug 07 HTML / CSS
CSS实现漂亮的时钟动画效果的实例代码
Mar 30 HTML / CSS
html中相对位置与绝对位置的具体使用
May 15 HTML / CSS
CSS 鼠标选中文字后改变背景色的实现代码
May 21 HTML / CSS
html原生table实现合并单元格以及合并表头的示例代码
May 07 #HTML / CSS
html解决浏览器记住密码输入框的问题
May 07 #HTML / CSS
使用CSS实现百叶窗效果示例代码
使用CSS实现按钮边缘跑马灯动画
使用CSS实现音波加载效果
table不让td文字溢出操作方法
Dec 24 #HTML / CSS
table设置超出部分隐藏,鼠标移上去显示全部内容的方法
Dec 24 #HTML / CSS
You might like
Wordpress 相册插件 NextGEN-Gallery 添加目录将中文转为拼音的解决办法
2010/12/29 PHP
PDO防注入原理分析以及注意事项
2015/02/25 PHP
PHP socket 模拟POST 请求实例代码
2016/07/18 PHP
PHP如何实现订单的延时处理详解
2017/12/30 PHP
php实现数字补零的方法总结
2018/09/12 PHP
详解PHP实现支付宝小程序用户授权的工具类
2018/12/25 PHP
AeroWindow 基于JQuery的弹出窗口插件
2011/06/27 Javascript
一个不错的字符串转码解码函数(自写)
2014/07/31 Javascript
node.js中的fs.realpath方法使用说明
2014/12/16 Javascript
node.js中的fs.fchmodSync方法使用说明
2014/12/16 Javascript
jQuery文件上传控件 Uploadify 详解
2016/06/20 Javascript
JS判断输入的字符串是否是数字的方法(正则表达式)
2016/11/29 Javascript
Easyui和zTree两种方式分别实现树形下拉框
2017/08/04 Javascript
详解node单线程实现高并发原理与node异步I/O
2017/09/21 Javascript
JavaScript for循环 if判断语句(学习笔记)
2017/10/11 Javascript
axios发送post请求springMVC接收不到参数的解决方法
2018/03/05 Javascript
JS点击动态添加标签、删除指定标签的代码
2018/04/18 Javascript
修改node.js默认的npm安装目录实例
2018/05/15 Javascript
vue+axios+mock.js环境搭建的方法步骤
2018/08/28 Javascript
Angular使用cli生成自定义文件、组件的方法
2018/09/04 Javascript
vue  自定义组件实现通讯录功能
2018/09/30 Javascript
Javascript 实现 Excel 导入生成图表功能
2018/10/22 Javascript
Vue 无限滚动加载指令实现方法
2019/05/28 Javascript
你不可不知的Vue.js列表渲染详解
2019/10/01 Javascript
[01:30:54]《加油DOTA》 第三期
2014/08/18 DOTA
Django中celery执行任务结果的保存方法
2019/07/12 Python
python爬虫模块URL管理器模块用法解析
2020/02/03 Python
Python项目打包成二进制的方法
2020/12/30 Python
美国糖果店:Sugarfina
2019/02/21 全球购物
Hotels.com泰国:酒店预订网站
2019/11/20 全球购物
仓库管理员岗位职责
2014/03/19 职场文书
岗位安全生产责任书
2014/07/28 职场文书
大学生创业事迹材料
2014/12/30 职场文书
升学宴家长致辞
2015/07/27 职场文书
入党心得体会
2019/06/20 职场文书
创业计划书之个人工作室
2019/08/22 职场文书