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

浏览器渲染过程 #3

Open
deepthan opened this issue Dec 27, 2017 · 0 comments
Open

浏览器渲染过程 #3

deepthan opened this issue Dec 27, 2017 · 0 comments

Comments

@deepthan
Copy link
Owner

deepthan commented Dec 27, 2017

前端关心的三大主题:可访问性、加载性能(和浏览器的渲染机制关系密切)和重构灵活

一. 浏览器如何渲染页面

浏览器请求、加载、渲染一个页面,会经历:

  1. DNS 查询
  2. TCP 连接
  3. HTTP 请求即响应
  4. 服务器响应
  5. 客户端渲染(本文内容)
  • 1.1 浏览器解析

解析过程

  1. 浏览器通过请求的URL进行域名解析到ip,向服务器发起请求,接收文件(HTML/SVG/XHTML、CSS、JS、IMG等) ;
  2. 解析HTML/SVG/XHTML并构建 DOM Tree(Webkit有三个C++的类对应这三类文档);
  3. 解析CSS会产生CSS规则树(CSS Rule Tree);
  4. JavaScript脚本文件加载后,通过DOM API和 CSSOM API来操作 DOM Tree 和CSS Rule Tree。

DOM解析成DOM Tree

<html>
<head>
    <title>DOM解析</title>
</head>
<body>
    <div>
        <h3>标题</h3>
        <p>内容1
            <span class='second-detail'>内容2</span>
        </p>
        
    </div>
</body>
</html>

解析结果:

graph LR
html--> head
head--> title
title--> DOM解析
html--> body
body--> div
div--> h3
h3--> 标题
div--> p
p--> 内容1
p--> span
span--> 内容2

CSS解析成 CSS Rule Tree

div { margin-left:1px } /* 用 1 代表它 */
h3 { margin-left:2px } /* 用 2 代表它 */
p { margin-left:3px } /* 用 3 代表它 */
.second-detail { margin-left:4px } /* 用 4 代表它 */

解析结果:B: 1 的意思是规则节点指向的具体规则。

graph LR
A:null--> B:1
A:null--> C:2
A:null--> D:3
D:3--> E:4

CSS与DOM结合的上下文树,div:B 的意思是节点名指向的规则节点

graph LR
html:A--> body:A
body:A--> div:B
div:B--> h3:C
div:B--> p:D
p:D--> 内容1:A
p:D--> span:E

  • 1.2 浏览器渲染
  1. 计算CSS样式
  2. 解析完成后浏览器引擎通过 DOM Tree 和 CSS Rule Tree 来构建Renderting Tree
  3. Flow或Layout :通过CSS Rule Tree 匹配 DOM Tree进行定位坐标和大小,是否换行,以及position、overflow、z-index等等属性。
  4. 最终通过调用Native GUI 的API绘制网页画面的过程称为Paint。

注意: Rendering Tree 渲染树并不等同于DOM树,因为一些像Header或display:none的 东西就不在渲染树中了。

  • 1.3 简要概括整个过程:
  1. 检查HTML并构建DOM
  2. 检查CSS并构建CSSOM
  3. Web浏览器将DOM和CSSOM结合,并构建出render tree
  4. 调用Native GUI 的API绘制Web页面
  • 1.4 重绘(repaint)和回流(reflow)

当用户浏览网页时进行交互或JS脚本改变页面结构时,页面会进行重新渲染: 重绘或回流。

重绘

元素的几何尺寸没有变,只是屏幕的一部分要重画。
如改变元素的颜色,背景色等。

回流

元素的几何尺寸改变时会影响到其他元素的位置或结构时页面需要重新验证并计算Render Tree。
以下可能会产生 Reflow:

  • 一些Javascript操作DOM Tree(增加/删除等)
  • css属性发生变化
  • 元素尺寸改变
  • 文本内容改变
  • 浏览器窗口改变大小或拖动
  • 动画效果进行计算和改变 CSS 属性值
  • 伪类激活(:hover)
  • 修改网页的默认字体

如图片载入是一个可以避免的reflow—— 构建DOM tree的时候遇到img标签会直接读取后面的内容而不是等待图片加载完再进行,这样会导致图片加载完了会进行回流,之前浏览器所做的都是无用功。解决办法是设置宽度和高度这样浏览器就知道了图片的占位面积,在载入图片前就预留好了位置。

注:display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化。

有fixed的背景图,或是有些元素不跟着滚动,滚屏也有可能会造成reflow。

性能比较

  HTML 使用的是 flow based layout ,也就是流式布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫 Reflow.  Reflow 的成本比 Repaint 的成本高得多的多。一个结点的 Reflow 很有可能导致子结点,甚至父点以及同级结点的 Reflow 。

  浏览器不会你每改一次样式,它就 Reflow 或 Repaint 一次。一般来说,浏览器会把这样的操作积攒一批,然后做一次 Reflow ,这又叫异步 reflow 或增量异步 Reflow 。但是有些情况浏览器是不会这么做的,比如:Resize 窗口,改变了页面默认的字体,等。对于这些操作,浏览器会马上进行 Reflow 。

  但是有些时候,我们的脚本会阻止浏览器这么干,比如:如果我们请求下面的一些DOM值:

offsetTop,
offsetLeft, 
offsetWidth, 
offsetHeight,
scrollTop/Left/Width/Height,
clientTop/Left/Width/Height,
IE中的 getComputedStyle(), 或 currentStyle

因为,如果我们的程序需要这些值,那么浏览器需要返回最新的值,而这样一样会flush出去一些样式的改变,从而造成频繁的reflow/repaint。

二. 优化渲染速度

  1. 指定文档编码类型,如 <meta charset="utf-8">;
  2. CSS 样式应该包含在 中, Javascript 脚本出现在 末尾;
  3. 减少 CSS 嵌套层级和选择适当的选择器;
    CSS选择器对性能的影响源于浏览器匹配选择器和文档元素时所消耗的时间。它是从右到左进行规则匹配的。
    .nav > p {font-size:12px},浏览器必须遍历页面中所有的 a 元素并且确定其父元素的 class 是否为 nav 。我们把最右边选择符(这里是p)称之为关键选择器。
    Google 资深web开发工程师 Steve Souders 对 CSS 选择器的执行效率从高到低做了一个排序:
1.id选择器(#)
2.类选择器(.)
3.标签选择器(div)
4.相邻选择器(div+p)
5.子选择器(div < p)
6.后代选择器(div p)
7.通配符选择器(*)
8.属性选择器(div[attr="attr"])
9.伪类选择器(div:nth-child)
  • 避免使用通用选择器 *
  • 避免使用标签或 class 选择器限制 id 选择器 div#nav , .nav#nav
  • 避免使用标签限制 class 选择器 div.nav
  • 避免使用多层标签选择器。使用 class 选择器替换,减少css查找
  • 避免使用子选择器
  • 使用继承(继承性:从自己开始到自己后代最小的元素),color text-开头的,line-开头的 font-开头的 都是继承的,关于盒子的 定位的,布局的都是不能继承的。
  1. 不要通过 JS 逐条修改 DOM 的样式,提前定义好 CSS 的 Class 进行操作;
  2. 尽量减少将 DOM 节点属性值放在循环当中,会导致大量读写此属性值;
  3. 尽可能的为产生动画的 HTML 元素使用 fixed 或 absolute 的 position ,那么修改他们的 CSS 是不会 Reflow 的;
  4. DOM树要小,CSS尽量用id和class,千万不要过渡层叠下去。
  5. 千万不要使用table布局。因为可能很小的一个小改动会造成整个table的重新布局。
  6. 尽可能的修改层级比较低的DOM。当然,改变层级比较底的DOM有可能会造成大面积的reflow,但是也可能影响范围很小

三. 相关名字详解

3.1 CSSOM

    CSS Object Model,css对象模型。它将样式表中的规则映射到页面对应的元素上。CSSOM识别tokens并把这些tokens转换成一个树结构上的对应的结点。所有结点以及它们所关联的页面中的样式就是所谓的CSS Object Model。
    有啥作用呢?

  • CSSOM 阻止任何东西渲染
    所有的css都是组织渲染的,因为css没处理好之前如果显示页面的话元素样式会一直在变化中,所以页面会等待所有css检查完再展示页面。CSSOM被用作创建render tree,如果不能高效的利用CSS会导致页面在加载时白屏。
  • CSSOM 在加载一个新页面时必须重新构建
    即使css文件被缓存了,但是只会节省加载css的时间,CSSOM却会重新构建,所以让页面加载速度变快的方法是写出好的css。
  • 页面中CSS的加载和页面中javascript的加载是有关系的
    javascript的加载很可能会阻塞CSSOM的构建(js可以改变css),在 CSSOM构建好之前页面上什么都不会展示出来(白屏)。

工具

  1. 查看浏览器渲染成本 speedTracer

如何让页面加载得更快:

  1. Optimize your critical rendering path
  2. Make sure your pages are calling stylesheets before javascripts.
  3. Ensure you are optimizing CSS delivery.
  4. Google – Web Performance Best Practices
  5. Yahoo – Best Practices for Speeding Up Your Web Site
  6. Steve Souders – 14 Rules for Faster-Loading Web Sites

参考文章

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant