一、css渲染原理(Cascading Style Sheets)
在介绍css渲染原理之前,我们简明扼要介绍一下页面的加载过程浏览器渲染过程,有助于更好理解后续css渲染原理。
1.1页面的加载过程和浏览器渲染过程
1.1.1页面加载过程

要点如下:

  • 浏览器根据DNS服务器得到域名的IP地址
  • 向这个IP的机器发送HTTP请求
  • 服务器收到、处理并返回HTTP请求
  • 浏览器得到返回内容(其实就是一堆HTML格式的字符串,因为只有HTML格式浏览器才能正确解析,这是W3C标准的要求。接下来就是浏览器的渲染过程。)
    1.1.2 浏览器渲染过程
    第一步:html经过HTML parser解析为DOM tree;(浏览器会把HTML结构字符串解析转换DOM树形结构。)
    第二步:css根据css规则经过css解析器解析为 style Rules(CSSOM tree);
    第三步:两棵树经过attachment结合Render Tree(形成一颗大树,此时它还是一颗迷茫的树,不知道节点的内容和位置)
    第四步:render tree (渲染树)经过Layout计算DOM的位置以及样式;
    第五步: 讲计算好的页面paint画出来;
    第六步:显示到浏览器上;
    流程图:

    简而言之,浏览器就是解析DOM生成DOM Tree,结合CSS生成CSS Tree,最终组成render tree,再渲染页面。(构建DOM->构建CSSOM->构建渲染树->布局->绘制)
    1.1.3影响DOM树构建的因素(js和css)
    先做个总结,然后再进行具体的分析:
    CSS不会阻塞DOM的解析,但是会影响JAVAScript的运行,javaSscript会阻止DOM树的解析,最终css(CSSOM)会影响DOM树的渲染,也可以说最终会影响渲染树的生成。
    接下来我们先看javascript对DOM树构建和渲染是如何造成影响的,分成三种类型来讲解:
    1、JavaScript脚本再html页面中
<html>
<body>
<div>1</div>
<script>
let div1 = document.getElementsByTagName('div')[0]
div1.innerText = 'time.geekbang'
</script>
<div>test</div>
</body>
</html>

我在两段div中间插入了一段JavaScript脚本,这段脚本的解析过程就有点不一样了。
通过前面DOM生成流程分析,我们已经知道当解析到script脚本标签时,其DOM树结构如下所示:

这时候HTML解析器暂停工作,javascript引擎介入,并执行script标签中的这段脚本,因为这段javascript脚本修改了DOM中第一个div中的内容,所以执行这段脚本之后,div节点内容已经修改为time.geekbang了。脚本执行完成之后,HTML解析器回复解析过程,继续解析后续的内容,直至生成最终的DOM。
以上过程应该还是比较好理解的,不过除了页面中直接内嵌JavaScript脚本之外,我们还通常需要在页面中引入JAVAScript文件,这个解析过程就稍微复杂了些,如下面案例:
2、html页面中引入javaScript文件

//foo.js
let div1 = document.getElementsByTagName('div')[0]
div1.innerText = 'time.geekbang'<html>
<body>
<div>1</div>
<script type="text/javascript" src='foo.js'></script>
<div>test</div>
</body>
</html>

这段代码的功能还是和前面那段代码是一样的,不过这里我把内嵌JavaScript脚本修改成了通过javaScript文件加载。其整个执行流程还是一样的,执行到JAVAScript标签时,暂停整个DOM的解析,执行javascript代码,不过这里执行javascript时,需要现在在这段代码。这里需要重点关注下载环境,因为javascript文件的下载过程会阻塞DOM解析,而通常下载又是非常耗时的,会受到网络环境、javascript文件大小等因素的影响。
优化机制:
不过谷歌浏览器做了很多优化,其中一个主要的优化就是预解析操作。当渲染引擎收到字节流之后,会开启一个预解析线程,用来分析HTML文件中包含的JavaScript、CSS等相关文件,解析到相关文件之后,会开启一个预解析线程,用来分析HTML文件中包含的javascprit、css等相关文件、解析到相关文件之后,预解析线程会提前下载这些文件。
再回到DOM解析上,我们知道引入javascprit线程会阻塞DOM,不过也有一些相关的策略来规避,比如使用CDN来加速Java
Script文件的家在速度,压缩javascript、CSS等相关文件,解析到相关文件之后,预解析线程会提前下载这些文件。
再回到 DOM 解析上,我们知道引入 JavaScript 线程会阻塞 DOM,不过也有一些相关的策略来规避,比如使用 CDN 来加速 JavaScript 文件的加载,压缩 JavaScript 文件的体积。另外,如果 JavaScript 文件中没有操作 DOM 相关代码,就可以将该 JavaScript 脚本设置为异步加载,通过 async 或 defer 来标记代码,使用方式如下所示:

<script async type="text/javascript" src='foo.js'></script>
或者
<script defer type="text/javascript" src='foo.js'></script>

async和defer区别:
async:脚本并行加载,加载完成之后立即执行,执行时机不确定,仍有可能阻塞HTML解析,执行时机在load事件派发之前。
defer:脚本并行加载,等待HTML解析完成之后,按照加载顺序执行脚本,执行时机DOMContentLoaded事件派发之前。
3、html页面中有css样式
案例:

//theme.css
div {color:blue}<html><head><style src='theme.css'></style></head>
<body><div>1</div><script>let div1 = document.getElementsByTagName('div')[0]div1.innerText = 'time.geekbang' // 需要 DOMdiv1.style.color = 'red' // 需要 CSSOM</script><div>test</div>
</body>
</html>

该示例中,JavaScript 代码出现了 div1.style.color = ‘red’ 的语句,它是用来操纵 CSSOM 的,所以在执行 JavaScript 之前,需要先解析 JavaScript 语句之上所有的CSS 样式。所以如果代码里引用了外部的 CSS 文件,那么在执行 JavaScript 之前,还需要等待外部的 CSS 文件下载完成,并解析生成 CSSOM 对象之后,才能执行 JavaScript 脚本。
而 JavaScript 引擎在解析 JavaScript 之前,是不知道 JavaScript 是否操纵了 CSSOM的,所以渲染引擎在遇到 JavaScript 脚本时,不管该脚本是否操纵了 CSSOM,都会执行CSS 文件下载,解析操作,再执行 JavaScript 脚本。所以说 JavaScript 脚本是依赖样式表的,这又多了一个阻塞过程。
总结:
通过上面三点的分析,我们知道了 JavaScript 会阻塞 DOM 生成,而样式文件又会阻塞js的执行。
4、接下来我们再看CSS对DOM树构建和渲染的影响:(假设法)
假设css不影响DOM树的解析,这个时候你加载css的时候,很可能会修改下面DOM节点的样式,
如果css加载不阻塞render树渲染的话,那么当css加载完之后,render树可能又得重新重绘或者回流了,这就造成了一些没有必要的损耗。所以这个假设是不成立得。
所以我们得出:css加载不会阻塞DOM树的解析,但会阻塞render树的渲染(渲染时需等CSS加载完毕)

因此,从上面js和css对DOM树构建的分析得出,应该把CSS放在文档的头部,尽可能的提前加载CSS;把JS放在文档的尾部,这样JS也不会阻塞页面的渲染。CSS会和JS并行解析,CSS解析也尽可能的不去阻塞JS的执行,从而使页面尽快的渲染完成。
1.3 CSS渲染规则
css渲染规则,是从上到下,从右到左渲染的。这是为什么呢?举个例子:

<div><div class="jac"><p><span> 111 </span></p><p><span> 222 </span></p><p><span> 333 </span></p><p><span class='yellow'> 444 </span></p></div>
</div><style>div > div.jartto p span.yellow {color: yellow;}
</style>

我们按照「从左到右」的方式进行分析:

  1. 先找到所有 div 节点。
  2. 在 div 节点内找到所有的子 div,并且是 class = “jac”。
  3. 然后再依次匹配 p span.yellow 等情况。
  4. 遇到不匹配的情况,就必须回溯到一开始搜索的 div 或者 p 节点,然后去搜索下个节点,重复这样的过程。
    综上:这样的搜索过程对于一个只是匹配很少节点的选择器来说,效率是极低的,因为我们花费了大量的时间在回溯匹配不符合规则的节点。
    我们按照「从右向左」的方式进行分析:
    1.首先就查找到 class=“yellow” 的 span 元素。
    2 .接着检测父节点是否为 p 元素,如果不是则进入同级其他节点的遍历,如果是则继续匹配父节点满足 class=“jac” 的 div 容器。
    3.这样就又减少了集合的元素,只有符合当前的子规则才会匹配再上一条子规则。
    综上所述,我们可以得出结论:
    浏览器 CSS 匹配核心算法的规则是以从右向左方式匹配节点的,这样做是为了减少无效匹配次数,从而匹配快、性能更优。
    1.4 CSS选择器权值(优先级)
    示例代码一:
<div ><p id="box" class="text">Jartto's blog</p>
</div>
<style>#box{color: red;}.text{color: yellow;}
</style>

当你知道 「ID 选择器 > 类选择器 」的时候,答案不言自明。
示例代码二:

<div id="box"><p class="text">Jartto's blog</p>
</div>
<style>#box{color: red;}.text{color: blue;}
</style>

这里就考查到了规则「类选择器 > 继承」,ID 对文本来说是继承过来的属性,所以优先级不如直接作用在元素上面的类选择器。
!important > 行内样式(权重1000) > ID 选择器(权重 100) > 类选择器(权重 10) > 标签(权重1) > 通配符 > 继承 > 浏览器默认属性
权值,代表优先级,权值越大,优先级越高。同种类型的选择器权值相同,后定义的选择器会覆盖先定义的选择器。注:组合使用,权值会叠加
二、优化策略
对于绝大部分的开发者来说,css的功能就是完成页面布局,制定页面的展示效果。其实css也有许多实现Web性能优化的方法。
我们都知道对于网站来说,性能至关重要,CSS作为页面渲染和内容展现的重要环节,影响着用户对整个网站的第一体验。因此,与其相关的性能优化是不容忽视的。
2.1 CSS 书写顺序对性能有影响吗?
需要注意的是:浏览器并不是一获取到 CSS 样式就立马开始解析,而是根据 CSS 样式的书写顺序将之按照 DOM 树的结构分布渲染样式,然后开始遍历每个树结点的 CSS 样式进行解析,此时的 CSS 样式的遍历顺序完全是按照之前的书写顺序。
在解析过程中,一旦浏览器发现某个元素的定位变化影响布局,则需要倒回去重新渲染。
栗子:

width: 150px;
height: 150px;
font-size: 24px;
position: absolute;

当浏览器解析到 position 的时候突然发现该元素是绝对定位元素需要脱离文档流,而之前却是按照普通元素进行解析的,所以不得不重新渲染。
渲染引擎首先解除该元素在文档中所占位置,这就导致了该元素的占位情况发生了变化,其他元素可能会受到它回流的影响而重新排位。
我们对代码进行调整:

position: absolute;
width: 150px;
height: 150px;
font-size: 24px;

在实际开发过程中,我们如何能保证自己的书写顺序是最优呢?
这里有一个规范,建议顺序大致如下:

  1. 定位属性position display float left top right bottom overflow clear z-index
  2. 自身属性width height padding border margin background
  3. 文字样式font-family font-size font-style font-weight font-varient color
  4. 文本属性text-align vertical-align text-wrap text-transform text-indent text-decoration letter-spacing word-spacing white-space text-overflow
  5. CSS3 中新增属性content box-shadow border-radius transform
    总之,我们需要知道这个规则就够了,剩下的可以交给一些插件去做,譬如 CSSLint
    2.2 有选择地使用选择器
    大家都知道css选择器的匹配是从右向左进行的,这一策略导致不同种类的选择器之间的性能也存在差异。
    举个例子:
    相比于#markdown-content-h3,显然使用#markdown .content h3时,浏览器生成渲染树(render-tree)所要花费的时间更多。因为后者需要先找到DOM中的所有h3元素,再过滤掉祖先元素不是.content的,最后过滤掉.content的祖先不是#markdown的。试想,如果嵌套的层级更多,页面中的元素更多,那么匹配所要花费的时间代价自然更高。
    不过现代浏览器在这一方面做了很多优化,不同选择器的性能差别并不明显,甚至可以说差别甚微。此外不同选择器在不同浏览器中的性能表现8也不完全统一,在编写CSS的时候无法兼顾每种浏览器。鉴于这两点原因,我们在使用选择器时,只需要记住以下几点即可。
    **1、保持简单,不要使用嵌套过多过于复杂的选择器。**例:
/* Bad */
div > div > div > p {color:red;}
/* Good */
p-class{color:red;}

2、通配符和属性选择器效率最低,需要匹配的元素最多,尽量避免使用。
只用通配符设定一切基础的样式,如:* {margin:0; padding:0;}这种方式,代码少,但是性能差,因为渲染的时候,要匹配页面上所有的元素!很多基础样式没有margin和padding的元素,比如div,li等。都被匹配,完全没必要!
3、不要在id选择器前使用标签名,id选择器本身就能唯一确定一个元素,没必要再在前面加上标签名,这样多此一举,还会降低效率。
如:div #box { color: white; } div 可不加
4、不要为了追求速度而放弃可读性与可维护性。
2.3 把stylesheets放在HTML页面头部
浏览器所有的stylesheets加载完成之后,才会开始渲染整个页面。在这之前,浏览器不会渲染页面的任何内容,页面会一直呈现空白。(因为要把stylesheets放在头部的原因)
如果放在HTML页面底部,页面渲染就不仅仅实在等在stylesheets的加载,还要等待html内容的加载完成,这样导致用户看到页面的时间会更晚。
2.4 减少使用昂贵的属性
在浏览器绘制屏幕时,所有需要浏览器进行操作或计算的属性相对而言都需要花费更大的代价。当页面发生重绘时,它们会降低浏览器的渲染性能。所以在编写CSS时,我们应该尽量减少使用昂贵属性,如box-shadow/border-radius/filter/透明度/:nth-child等。
当然,并不是让大家不要使用这些属性,因为这些应该都是我们经常使用的属性。之所以提这一点,是让大家对此有一个了解。当有两种方案可以选择的时候,可以优先选择没有昂贵属性或昂贵属性更少的方案,如果每次都这样的选择,网站的性能会在不知不觉中得到一定的提升。
2.5 避免使用@important命令
不建议使用@import主要有以下两点原因。
首先,使用@import引入CSS会影响浏览器的并行下载。使用@import引用的CSS文件只有在引用它的那个css文件被下载、解析之后,浏览器才会知道还有另外一个css需要下载,这时才去下载,然后下载后开始解析、构建render tree等一系列操作。这就导致浏览器无法并行下载所需的样式文件。
其次,多个@import会导致下载顺序紊乱。在IE中,@import会引发资源文件的下载顺序被打乱,即排列在@import后面的js文件先于@import下载,并且打乱甚至破坏@import自身的并行下载。
所以不要使用这一方法,使用link标签就行。
(1)使用@import引用外部CSS文件
导入式

<style type="text/css">@import url("css文件路径");
</style>

(2)使用link引用外部CSS文件(推荐此方法)
链接式

<link type="text/css" rel="styleSheet" href="CSS文件路径" />

接下来大致说一下这两种引用外部css文件方式的区别:
link:1、属于XHTML 2、优先加载CSS文件到页面
@import1、属于CSS2.1 2、先加载HTML结构在加载CSS文件。
总结:@import相当于就是把标签放在页面的底部,所以从优化性能的角度来看,应该尽量避免使用@important命令。
2.6 优化回流与重绘
首先我们来了解一下什么是回流(reflow)和重绘(repaint),
a.重绘:
重绘是指css样式的改变,但是元素的大小和尺寸不变,而导致节点的重新绘制。
b.回流:
回流(reflow)是指元素的大小、位置发生了改变,而导致了布局的变化,从而导致了布局树的重新构建和渲染。
2.6.1 减少回流
重排会导致浏览器重新计算整个文档,重新构建渲染树,这一过程会降低浏览器的渲染速度。如下所示,有很多操作会触发回流,我们应该避免频繁触发这些操作。

  1. 改变font-size和font-family
  2. 改变元素的内外边距
  3. 通过JS改变CSS类
  4. 通过JS获取DOM元素的位置相关属性(如width/height/left等)
  5. CSS伪类激活
  6. 滚动滚动条或者改变窗口大小
    此外,某些CSS属性具有更好的回流性能。如使用Flex时,比使用inline-block和float时重排更快,所以在布局时可以优先考虑Flex。
    2.6.2 避免不必要的重绘
    当元素的外观(如colorbackgroundvisibility等属性)发生改变时,会触发重绘。在网站的使用过程中,重绘是无法避免的。不过,浏览器对此做了优化,它会将多次的重排、重绘操作合并为一次执行。不过我们仍需要避免不必要的重绘,如页面滚动时触发的hover事件,可以在滚动的时候禁用hover事件,这样页面在滚动时会更加流畅。
    此外,我们编写的CSS中动画相关的代码越来越多,我们已经习惯于使用动画来提升用户体验。我们在编写动画时,也应当参考上述内容,减少重绘重排的触发。
    最后需要注意的是,用户的设备可能并没有想象中的那么好,至少不会有我们的开发机器那么好。我们可以借助Chrome的开发者工具进行CPU降速,然后再进行相关的测试,降速方法如下图所示。

    如果需要在移动端访问的,最好将速度限制更低,因为移动端的性能往往更差。
    2.7 去除无用的CSS
    一般情况下,会存在这两种无用的CSS代码:一种是不同元素或者其他情况下的重复代码,一种是整个页面内没有生效的CSS代码。对于前者,在编写的代码时候,我们应该尽可能地提取公共类,减少重复。对于后者,在不同开发者进行代码维护的过程中,总会产生不再使用的CSS的代码,当然一个人编写时也有可能出现这一问题。而这些无用的CSS代码不仅会增加浏览器的下载量,还会增加浏览器的解析时间,这对性能来说是很大的消耗。所以我们需要找到并去除这些无用代码。

CSS渲染原理,优化策略相关推荐

  1. CSS 渲染原理以及优化策略

    大厂技术  高级前端  Node进阶 点击上方 程序员成长指北,关注公众号 回复1,加入高级Node交流群 推荐理由:由浅入深,鞭辟入里. 提起 CSS 很多童鞋都很不屑,尤其是看到 RedMonk ...

  2. 前端性能优化之浏览器渲染原理和关键渲染路径、复合线程、图层及优化、JS 开销及优化和 HTML 和 CSS 优化

    一.浏览器渲染原理和关键渲染路径 浏览器构建渲染树,DOM 树和 CSSDOM 树合成为 Render Tree 渲染树. 浏览器的渲染流程,如下所示: JavaScript -> Style ...

  3. 【优化】1141- 网页渲染性能优化 —— 渲染原理

    作者:晨风明悟 链接:https://zhuanlan.zhihu.com/p/39879808 渲染原理 在讨论性能优化之前,我们有必要了解一些浏览器的渲染原理.不同的浏览器进行渲染有着不同的实现方 ...

  4. react如何遍历并比较_[前端进阶] 这可能是最通俗易懂的React 渲染原理及性能优化...

    如今的前端,框架横行,出去面试问到框架是常有的事. 我比较常用React, 这里就写了一篇 React 基础原理的内容, 面试基本上也就问这些, 分享给大家. React 是什么 React是一个专注 ...

  5. 从渲染原理谈前端性能优化

    前言 合格的开发者知道怎么做,而优秀的开发者知道为什么这么做. 这句话来自<web性能权威指南>,我一直很喜欢,而本文尝试从浏览器渲染原理探讨如何进行性能提升. 全文将从网络通信以及页面渲 ...

  6. 浏览器渲染原理以及性能优化

    浏览器渲染原理以及性能优化 浏览器渲染原理 进程与线程 Request请求阶段 Response响应阶段 浏览器渲染网页注意事项 浏览器渲染网页阻塞顺序 DOM的重绘和回流 Repaint & ...

  7. html图片视频渲染原理,初识浏览器渲染原理和CSS动画

    1.浏览器渲染原理 浏览器到底是怎么将一个html文件渲染成生动的页面的?话不多说,直接看图 以图中的过程,浏览器的渲染过程大致就是: 通过HTML解析将HTML文件解析为 DOM 树; 通过CSS解 ...

  8. 前端浏览器渲染原理及优化

    文章目录 一.浏览器组成 1. 对浏览器内核的理解 2. 浏览器的主要组成部分 二.浏览器渲染原理 1.浏览器的渲染过程 步骤一: 步骤二: 步骤三: 步骤四: 步骤五: 2.相关概念 ①重排(更新元 ...

  9. 【浏览器渲染原理】步骤及优化

    [浏览器渲染原理]步骤及优化 文章目录 [浏览器渲染原理]步骤及优化 一.对浏览器内核的理解 二.浏览器的主要组成部分 三.浏览器的渲染过程 四.浏览器渲染优化 ① 针对JavaScript: ② 针 ...

最新文章

  1. linux wifi 报错 siocsifflags: operation not possible due to rf-kill
  2. 题目1090:路径打印
  3. linux里ip地址正常但远程连接不上,linux系统上解决postgres远程连接不上的问题
  4. linux把终端嵌入桌面,在Ubuntu Linux桌面上嵌入终端窗口
  5. 32--数组中重复的数字
  6. PHP基于phpqrcode类生成二维码
  7. 1024 程序员节:给 DBA 们的福音
  8. mysql中like与rlike_MySQL中RLIKE运算符的使用详解-mysql教程-
  9. Python扩展库numpy中的布尔运算
  10. 栅栏密码怎么写java程序_简单栅栏密码加密器的JSF版
  11. Yii在控制层中引入模版进行渲染的几种方式。
  12. Linux ida 代码,[分享][下载]IDA Pro 7.5 Linux Demo
  13. 基于分解的MOEA的理解
  14. 测试手机端APP时,模拟网速慢的情况
  15. SQL Server evaluation period has expired
  16. 恐惧迷宫小游戏抖音快手超火
  17. 爆笑!让你捧腹大笑的标语
  18. unity3d-unet小demo
  19. 关于RPC框架封装时ThreadPool的选型和设计
  20. 计算机音乐谱夜空中最亮的星歌词,夜空中最亮的星歌谱及歌词

热门文章

  1. UUS网络电视2008下载-UUS最新版抢先体验
  2. 电子政务与企业信息化
  3. Python入门学习 DAY 05 字符串,列表的内置方法
  4. Python装饰器、内置函数之金兰契友
  5. 苹果笔记本不能用windows吗 如何让苹果电脑支持windows
  6. 重庆宽带公网IPv6测试
  7. 用户怎样访问文件服务器,文件服务器用户访问
  8. apt 连接夜神模拟器,系统提示:nox_apt.exe已停止运行 的解决办法
  9. 云计算和大数据应用技术免费讲座
  10. easy-excel 根据模板生成 excel 报表