css渲染原理
- 浏览器渲染原理
- 浏览器接收到服务器返回的html页面。
- 浏览器开始构建DOM树(DOM TREE),遇到css样式会构建CSS规则树(css RULE TREE)。
- 遇到javascript会通过DOM API和CSSDOM API来操作DOM Tree和CSS Rule Tree。
- 解析完成后,浏览器会通过DOM Tree和CSS Rule Tree来构造Rendering Tree(渲染树)。
- 最后,渲染树构建完成后就是“布局”处理,也就是确定每个节点在屏幕上的确切显示位置。
- 渲染之后,开始“绘制”,遍历渲染树,并用UI后断层,将每一个节点绘制出来。
- css渲染规则
- css的渲染规则,是从上到下,从右到左渲染的。
.main h4 a {font-size: 14px}
- 渲染过程是这样的:首先先找到所有的a,沿着a的父元素查找h4,然后再沿着h4,查找.main。中途找到了符合匹配规则的节点就加入结果集。如果找到根元素的html都没有匹配,则这条路径不再遍历。下一个a开始重复这个查找匹配,直到没有a继续查找。
-浏览器的这种查找规则就是为了尽早过滤掉一些无关的样式规则和元素。 - 页面渲染与执行过程
- 通过一个例子,我们来详细看看页面渲染和执行过程到底是怎么工作的:
<html>
<body>
<link href="example.css" rel="stylesheet">
<div>Hi here</div>
<script>
document.write('<script src="other.js"><scr' + 'ipt>')
</script>
<div>Hi again</div>
<script src="last.js"></script>
</body>
</html>
- 解析器遇到了example.css,并将它从网络中下载下来。下载样式表的过程是耗时的,但是解析器并没有被阻塞,继续往下解析
- 解析器遇到<script>标签,但是由于样式文件下载未完成,阻塞了该脚本的执行(上面已指出)。解析器(构建DOM树和 CSS规则树)被阻塞住,不能继续往下解析
- 因为渲染树是DOM树 和 CSS规则树 来构造,所以此时,渲染树的构建也被阻塞
- 渲染树是绘制触发条件之一,因此Hi there!也就 不能被绘制(painting->display)到页面中。
- 绘制的触发条是: 渲染树构建完成,并遇到了阻塞
- 接下来,一旦example.css下载完成,内联的脚本执行完了之后,解析器开始执行遇到的<script>标签,并查到src属性中的other.js文件,立即被阻塞,开始下载other.js;
- 因为解析器被阻塞,触发了绘制条件,浏览器就会收到绘制的请求,“Hi,here”就会显示在页面上
- other.js加载完成了之后,解析器继续向下解析,遇到 last.js 之后又被阻塞,
- 因为解析器再次被阻塞,又形成了 “绘制” 条件,因此绘制DOM树的内容,即,“Hi,again”就会显示在页面上。
- last.js加载完成,并且被执行。
- css优化
-
把 Stylesheets 放在 HTML 页面头部
- 浏览器在所有的 stylesheets 加载完成之后,才会开始渲染整个页面,在此之前,浏览器不会渲染页面里的任何内容,页面会一直呈现空白。这也是为什么要把 stylesheet 放在头部的原因。如果放在 HTML 页面底部,页面渲染就不仅仅是在等待 stylesheet 的加载,还要等待 html 内容加载完成,这样一来,用户看到页面的时间会更晚。
- 对于 @import 和 <link> 两种加载外部 CSS 文件的方式:@import 就相当于是把 <link> 标签放在页面的底部,所以从优化性能的角度看,应该尽量避免使用 @import 命令
-
嵌套层级不要超过三级
- 一般情况下,嵌套层级不要超过三级,过渡的嵌套会导致代码变得臃肿,导致css文件体积变大,造成性能上的浪费,影响渲染的速度;css层级太多,也过于依赖html DOM结构,不易于维护。如果层级比较深,就直接定义一个class代替多余的层级。
css 命名规范,书写规范。
-
多用继承属性
- 尽量的继承父类样式 ,重复的定义会造成很多不必要的性能浪费。
少用滤镜,好用hack,少用position: absolute;
使用简写样式。见下例
.mg { margin-top: 20px; margin-right: 0px; margin-bottom: 20px; margin-left: 100px; }
-
可以简写为
.mg {
margin: 20px 0px 20px 100px;
}
7. 不要再id选择器和class选择器前使用标签名 例如:
div #box {
color: white;
}
-
id选择器本身就能唯一确定一个元素,没必要再在前面加上标签名。同样,类选择器也是,如果需要给某个标签的类增添额外的样式,建议使用另一个类名。这种情况下,也可以使用 div .box{} 形式,差别不大。
平铺的背景图片不要过小,1px的图片平铺长宽25px的区域块,需要2500次,太影响渲染速度。
谨慎使用float,具体细节请参考我的另一片文章 《深入理解f浮动与清除浮动》
-
合理化布局(模块化布局)
- 可以把样式划分为”基类“ 和 “扩展类”; 把模块基本相同的样式写在基类里,不同的再重新用class定义,写在扩展类中。
尽量少用通配符,只用通配符设定一切基础的样式,如:
* {margin:0; padding:0;}