inside the-browser
TRANSCRIPT
下载
一个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 – 要求浏览器向服务器请求全新内容
解析
• Token List
• Normalized Token List
• DOM Tree
Demo
解析
输入 – 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
布局
流布局
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;