inside the-browser

35
Inside the Browser 浏览器渲染原理

Upload: jy03845581

Post on 13-Jul-2015

928 views

Category:

Technology


5 download

TRANSCRIPT

Inside the Browser

浏览器渲染原理

WHY?

HTML布局无处不在 已经不局限与浏览器

Summary

浏览器的工作过程

资源下载

HTML解析

CSS计算

布局

渲染

浏览器对细节的具体优化手段

Firefox

Webkit / Chrome

How?

下载

一个HTTP库能够搞定?

DNSClient

WebRequest

NetworkStream

HTMLParser

够了吗?

不仅仅一个文件!

<script src…

<link href…

<img src…

<iframe src…

何时开始下载它们?

下载

<head>中的<script>和<link>

服务器端Response.Flush()

<body>中的<script>

document.write

new Image().src = …

defer VS async

下载

资源优先级

link[rel=stylesheet] / script

object / img / iframe

link[rel=prefetch]

脚本依赖

下载阻塞 VS 执行阻塞

并行度

服务器压力 VS 客户端效率

http://www.otakustay.com/browser-strategy-loading-external-resource/

下载

Socket重用

Connection: keep-alive

Content-Length

Transfer-Encoding: chucked

正确性保证

Content-MD5

断点续传

Accept-Range

Content-Range

下载

BS的精髓 – 缓存

验证型缓存

Last-Modified & If-Modified-Since / If-Unmodified-Since

ETag & If-Match / If-None-Match

If-Range

非验证型缓存

Cache-Control

Expires

缓存失效

Vary / Via / Date / Age

http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html

下载

缓存年龄计算

age_value – Age响应头的值

date_value – Date响应头的值

request_time – 发起请求的本地时间

response_time – 收到响应的本地时间

now – 当前本地时间

apparent_age = max(0, response_time - date_value);

corrected_received_age = max(apparent_age, age_value);

response_delay = response_time - request_time;

corrected_initial_age = corrected_received_age + response_delay;

resident_time = now - response_time;

current_age = corrected_initial_age + resident_time;

下载

缓存过期计算

freshness_lifetime =

使用max-age时为max-age的秒数

使用Expires时为(Expires - Date)

response_is_fresh = (freshness_lifetime > current_age)

max-age=0 VS no-cache

max-age=0 – 要求浏览器向服务器验证缓存

no-cache – 要求浏览器向服务器请求全新内容

下载

输入 – 资源URI

输出 – HTML字符流

HTTP的超链接特性注定资源之间有关联的依赖

外部资源位置、类型不同影响下载时机

Response.Flush对下载的影响

缓存机制复杂但完善

解析

• Token List

• Normalized Token List

• DOM Tree

Demo

解析

只有这些? document.writ

e

解析

输入 – HTML字符流

输出 – DOM Tree

HTML无法用自顶向下或自底向上的方法解析

过程 – 序列化 -> 转义处理 -> 标签匹配

脚本执行会增加解析的回溯

DOM操作回溯至标签匹配过程

document.write回溯至序列化过程

CSS计算

元素 – 匹配样式

div>div>div>div>div…>div { color: red; }

dom.parentNode istanceof HTMLDivElement &&

div.parentNode.parentNode instanceof HTMLDivElement &&

div.parentNode.parentNode.parentNode instanceof …

问题

样式表很大,对内存造成压力

每个元素生成一个StyleObject浪费内存

查找元素匹配的样式消耗时间和CPU

如果是div div div div … div呢?

CSS计算

Webkit – 特定条件下样式共享

鼠标状态(:hover / down / clicked)相同

没有id

标签名相同

class名称相同

attribute均相同

链接状态(:link / :visited)相同

聚集状态(:active / :focus)相同

不能匹配属性选择器

没有内联样式

没有兄弟选择器(+ / :first-child / :last-child / …)

CSS计算

Firefox

Rule Tree + Style Context Tree

https://developer.mozilla.org/en/Style_System_Overview

Style sheets & rules

Rule tree

Style context tree

DEMO

CSS计算

Map

{ string: [ selector, selector, … ] }

以最右选择器为依据

匹配

查找id map

查找class map

查找general map

确定selector完全匹配

遍历general map

CSS计算

#title { … }

p.error { … }

input[type=radio] { … }

#nav li~li a { … }

.fix-clear * { … }

<div class=“fix-clear”><span id=“title” class=“error”>Erorr Occurred!</span>

</div>

CSS计算

CSS层级(优先级)

来源层级

浏览器UA样式

用户样式

作者样式

用户样式 + !important

作者样式 + !important

样式层级

1,1,1,1算法

inline(0/1), count(id), count(attribute), count(tag)

ul#nav ol li.red

CSS计算

DOM Tree

document

html

head

meta title

body

h1 p

Render Tree

htmlviewport

scrollblock

bodyblock

h1block

text

pblock

text

https://developer.mozilla.org/en/Mozilla_Style_System_Documentation

CSS计算

元素没有渲染对象

head / meta / script

元素有多个渲染对象

html / li

select / input[type=file]

通过CSS改变渲染对象

display: none

::before / ::after

CSS计算

输入 – DOM Tree

输出 – Render Tree

目标 – 内存优化、匹配效率优化

方法 – 样式共享、选择器索引

DOM Tree !== Render Tree

布局

流布局

display: inline / inline-block / block

float: left / right

clear: left / right / both

position: static / relative / absolute / fixed

HTML三条流

文档流、浮云流、定位流

其它因素

display: list-item

display: run-inhttp://www.w3.org/TR/css3-box/#the-lsquo

布局

table布局

display: table / inline-table / table-row-group / table-

header-group / table-footer-group / table-row / table-

column-group / table-column / table-cell / table-caption

div VS table – 流布局 VS table布局

布局

坐标系 – 左上角为0,0点,右|下为正坐标

布局是递归过程

流布局可自左向右、自上而下进行,流中靠后的元素不会影响流中靠前的元素的布局(无回溯)

table布局需要回溯才能够完成(知道每一个单元格的大小,才能完成整个布局)

反对table布局的原因 – 回溯对渲染的影响

DEMO

布局

全局Reflow

整个Render Tree全部重新计算布局

全局布局样式变更 – body { font-size: 12px; } / 添加新样式表

窗口大小变化

局部Reflow

仅标识为needLayout的渲染元素计算布局

Render Tree中插入新的渲染元素

渲染元素属性变化

Reflow会引起另一个Reflow – Reflow导致滚动条位置变化

布局

同步Reflow

全局Reflow通常同步进行

读取offsetWidth/offsetHeight等属性

异步Reflow

局部Reflow通常异步进行

FireFox:Reflow任务进入线程Queue,任务调度器负责执行

Webkit:定时器定时遍历Render Tree,布局所有needLayout对

Reflow任务可合并,一次脚本执行过程中多个样式修改仅做一

次Reflow,但是有阀值

DEMO

布局

父元素确定自己的宽度

开始遍历子元素

指定子元素渲染器的x/y属性

判断子元素是否需要布局,调用layout函数

累计所有子元素的width/padding/border/margin,计算自己

的宽度

同时考虑availWidth / sizing-box / min-width / max-width

将needLayout改为false

布局

文字布局

text-align: justified

white-space: nowrap / pre / pre-wrap

overflow: hidden / visible

换行计算

每行一个line-box负责渲染

当需要换行时,通知父元素,父元素创建新的line-box并重新布局

换行算法与文化相关

英文单词不能断开

中文标点不能在行首

布局

有流式布局和table布局2种

table布局需要回溯,流式布局通常不需要

Reflow的分类

触发类型上 – 全局和局部

执行类型上 – 同步和异步

布局过程递归进行

坐标系为左上角0,0点,右、下为正方向

渲染

transform / filter / z-index / color / visibility…

Reflow VS Repaint – display: none VS visibility: hidden

渲染顺序(CSS2):

background color

background image

border

children

outline

http://www.w3.org/TR/CSS21/zindex.html

渲染

Firefox – display list

找到Render Tree中在指定Repaint区域内的渲染对象

Render Tree的渲染对象经Stacking Order排序后生成

[background(A), border(A), border(B), outline(A)]

避免多次遍历Render Tree

[background(A, B), border(A, B), outline(A, B)]

完全不可见的元素不可被加到display list中

display: none;

opacity: 0;

渲染

Webkit – rectangle storage

Repaint前先将原有矩形内容保存为位图

计算重绘后的矩形内容的位图

比对2个位图,仅绘制差异部分

Chrome的Repaint在独立进程中

进程间通过事件进行传递和响应

绘制监听器负责监听重绘事件,把事件委托给Render Tree的根

Repaint永远从Render Tree的根开始,遍历并找到需要重绘的节

点,对节点调用重绘(递归至子节点)