浏览器:渲染、重绘、重排两三事

作者: 空气 分类: 前端开发 发布时间: 2014-10-21 17:50 ė188 6没有评论

导读

怎样尽可能的缩短浏览器上页面渲染的时间?我们可以从多个个方面进行改善。写出高效的css代码、避免使用css表达式、把css文件放在页面顶部等等。对于接触Web前端内容不久的新人来说,优化页面渲染、提高页面渲染效率是一件困难的事情。本专题,从渲染引擎开始,剖析渲染、重排、重绘技巧,提升技术水平,精彩不容错过。

了解浏览器如何工作—渲染引擎

从基础架构上我们也可以看到浏览器的重头戏其实在于渲染引擎(又称排版引擎),很多页面兼容性问题的根源可以说也皆来源于此。360浏览器HTML5跑分再高(http://html5test.com/),UI与交互再怎么不一样,内核还是一样的。好了,那我们深入到渲染引擎内部仔细看一下吧。

渲染引擎(the rendering engine)简述

渲染引擎的职责,正如字面上的意思就是负责从服务器端返回的HTML,XML,或者IMAGES等资源的渲染工作并显示给最终用户。通过浏览器 插件(plug-in or browser extension)技术,它也能显示一些其他文档格式的资源,如PDF,后期的文章会针对这种技术进行一下说明,本章重点描述渲染引擎的首要功能,即通 过渲染引擎显示出经过CSS样式化的HTML和图片结果。

前面已经介绍过,firefox,chrome,safari 包括了两种渲染引擎,火狐浏览器使用gecko,safari跟chrome(后来opera跟进)使用webkit. Webkit是一个开源的渲染引擎,起初只能用于linux平台,后来苹果公司apple对其源代码进行了扩展改造,使其能运行与mac跟windows 平台,后起之秀chrome对其有进行了一些列扩充与推广,使其越来越成为标准流行的渲染网页引擎,webkit详细介绍可参见 http://webkit.org/。

基本渲染过程

用户请求的资源通过浏览器的网络层到达渲染引擎后,渲染工作开始。每次渲染文档通常不会超过8K的数据块,其中基础的渲染过程如下图所示:

1

渲染引擎首先解析HTML文档,转换为一棵DOM树,此为第一步。接下来不管是内联式,外联式还是嵌入式引入的CSS样式也会被解析,渲染出另 外一棵用于渲染DOM树的树-渲染树(render tree) ,渲染树包含带有颜色,尺寸等显示属性的矩形,这些矩形的顺序与显示顺序一致。然后就是对渲染树的每个节点进行布局处理,确定其在屏幕上的显示位置。最后 就是遍历渲染树并用上一章提到的UI后端层将每一个节点绘制出来。

以上步骤是一个渐进的过程,为了提高用户体验,渲染引擎试图尽可能快的把结果显示给最终用户。它不会等到所有HTML都被解析完才创建并布局渲染树。它会在从网络层获取文档内容的同时把已经接收到的局部内容先展示出来。

不同渲染引擎具体不同的渲染流程

上面只是介绍了渲染引擎一般的处理流程,针对不同的渲染引擎具体步骤可能有所不同,就拿常见的webkit跟gecko来说吧。

首先是webkit的详细渲染流程:

2

火狐等浏览器的gecko渲染流程:

3

从上面两幅图可以看出,尽管两者使用了不同的“专业术语”,但是从图上可以看出,两者的渲染过程可谓大同小异,正是于此,我们可以再把具体的过程统一分离出来,接下来的四章会根据四个主要渲染过程进行一下较为细致的说明。

原文链接:http://www.cnblogs.com/luluping/archive/2013/04/05/3000461.html

1、浏览器如何渲染文本

浏览器是我们最常用的软件之一,文本又是网页中最主要的元素,在浏览器显示文本的过程中有许多有趣的细节,值得展开来讲讲,或许能减少一些误解。这 是一个比较粗略的,概括性的介绍,尽可能不涉及过多的技术细节和具体实现,而立足于给 Web 开发者和设计师提供一些正确的概念。

下面的介绍主要根据我对 WebKit 和 Gecko (Firefox) 的印象来谈,其他的浏览器也大致相同,如有阙漏之处欢迎指出。

当浏览器收到来自 Web 服务器的网页数据之后,第一步是要把它解码成可以阅读的文本,因为历史原因,不同区域和语言的网页可能会使用不同的编码方式,而浏览器判断编码主要是依据以下方法:

  1. Web 服务器返回的 HTTP 头中的 Content-Type: text/html; charset= 信息,这一般有最高的优先级;
  2. 网页本身 meta header 中的 Content-Type 信息的 charset 部分,对于 HTTP 头未指定编码或者本地文件,一般是这么判断;
  3. 假如前两条都没有找到,浏览器菜单里一般允许用户强制指定编码;
  4. 部分浏览器 (比如 Firefox) 可以选择编码自动检测功能,使用基于统计的方法判断未定编码。

编码确定后,网页就被解码成了 Unicode 字符流,可以进行进一步的处理,比如 HTML 解析了,不过我们这里跳过 HTML/XML 解析的细节,单讲得到了解析后的文本元素之后该怎么处理。

因为我们得到的文本可能是很多种语言混杂的,里面可能有中文、有英文,它们可能要用不同的字体显示;也可能有阿拉伯文、希伯来文这种从右到左书写的 文字;也有可能涉及印度系文字这样涉及复杂布局规则的文字;另外,还可能有网页内自己指定的文本语言,比如 <span lang=”jp”>日本语</span> 这样的标记,使得日文汉字可以使用日文字体显示 (因为 Han Unification 导致这些汉字和中文里的汉字使用同样的代码点,尽管很多写法不同),”lang” 属性也可以在 HTTP 头、<meta> 或者 <html> 出现,用于标记整个文档的全局语言,通常这是一种好的习惯,方便浏览器进行字体匹配。

为了统一处理所有这些复杂的情况,我们要将文本分为由不同语言组成的小段,在有的文本布局引擎里,这个步骤称为“itemize”,分解后的文本段 常被称作“text run”,但是具体划分的规则可能根据不同的引擎有所区别,比如 HarfBuzz 和 ICU 一般是根据要使用的不同排版类来划分 (常称作“shaper”),比如英语和法语可能使用同一个 shaper 排版,那么相邻的英语和法语文本就会划分到同一个 run 里,而希伯来文需要另一个 shaper,就划分到它自己的 run 里,以 HarfBuzz 为例,它有这样一些 shaper:

  • 通用的 (适用于中文、英文等等大多数布局规则简单的语言)
  • 阿拉伯文
  • 希伯来文
  • 印度系文字
  • 高棉文
  • 缅文
  • 谚文

不少浏览器还会在这个划分下面,在确定具体使用的字体之后,根据使用字体的不同划分更细的 run,这种 run 可能称作“SimpleTextRun”,每个都会使用和相邻不同的字体,最后把它们逐一交给 shaper 进行排版得到要绘制的字形,这样一来,shaper 的工作就被简化为在确定的语言、确定的字体下排版确定的文本,生成对应的字形和它们应该放置的位置、占用的空间。下面先详细说说确定字体的步骤。

说到字体,首先必须提到的就是 CSS 里的 font 和 font-family 等规则。比如这样的规则:

如果对于这样一段文本:

表示这个段落里优先使用 Helvetica 这个 family 的字体,如果找不到,就找 Arial,如果还是找不到,就用浏览器设置的默认非衬线字体 (有的浏览器,比如 Safari 只给你一个设置,有的像 Firefox 则允许根据不同语言设置,这时可以根据前面分析得到的文本 run 语言信息来判断该用哪个),这个过程非常简单,大家都很好理解。稍微复杂一点的是“jumps”,它应该继承父元素的 font-family,也用 Helvetica,但不用默认的 Regular,而用 Bold 版本,假如找不到 Helvetica Bold,就找 Arial Bold,否则就找浏览器设置的那个字体的 Bold 版本,假如都没有呢?就要考虑用人工伪造的方式来显示粗体了,这个且按下不谈,先看对于中文常见的情况:CSS 指定的字体没有覆盖我们需要的文本时,该怎么做。比如还是上面的 CSS 规则,但对这样的文本:

这里的“一只敏捷的狐狸”该用什么字体呢?假设 CSS 里具体指定了中文字体,比如 Helvetica, STHeiti, sans-serif,那很简单,按照英文字体一样的规则来判断:逐个字符尝试当前的字体是否提供了针对该字符的字形,如果没有则尝试下一个,要是到了最 后都没找到匹配的字体呢?CSS 规范里只简单的说执行“system font fallback”,但这个过程在不同的浏览器下可能很不一样,比如 WebKit 会使用 font-family 列表里的第一个字体和这段文本所属的语言来寻找 fallback 字体,像 Times 这样的 serif 字体对应的中文 fallback 字体,在 Mac OS X 下是华文宋体 (STSong);而 Firefox 则会根据 sans-serif 这样的通用 font family 和对应的语言匹配到设置中针对对应语言的默认字体,比如在 Mac OS X 默认的中文非衬线字体是华文黑体 (STHeiti)。Linux 下一般通过 fontconfig 去根据语言、风格等参数来选择 fallback,但不同浏览器的实现还可能有区别;Windows 下则一般会使用系统的 Font Linking 机制,根据注册表内的 FontSubstitutes 信息来寻找。因为在这里不同的浏览器可能有不同的行为,所以建议在 CSS 中写明对应平台该用的字体。

具体的字体选择还有一些不太容易注意的细节,也是各个浏览器差异比较大的一点,可能会出现这样一些问题:

是否支持用字体的 PostScript name 选择:如 STHeiti 的 Light 版本又称作 STXihei,或者是否能用 full name 选择:有的浏览器不能正确地将 CSS 里对字体的 font-weight 或者 font-style 等要求映射到特定的字体上,尤其是在字体使用了非标准的 style 命名的情况下,考虑到很多厂商有自己的字体命名规则,这其实很容易出现,像 Helvetica Neue 的 UltraLight, Light, Regular, Medium, Bold 这些不同的 weight,是怎么对应到 CSS font-weight 的 100 到 900 数值上的?这就是特别容易出现 bug 的地方。

是否支持按 localized name 选择:比如能不能用 “宋体” 来代表 “SimSun”。以 Mac OS X 下的浏览器为例,Firefox 支持这样的写法,但基于 WebKit 的浏览器一般不支持,这样的问题 CSS 规范没有限定,所以无论哪种情况都是允许的。

总的说来,如果要保证最大限度的兼容性,在 CSS 书写的时候应该尽可能选择明确、不容易出错的写法,尽量少隐式地让浏览器自己确定 (be explict instead of implict),虽然隐式写法通常比较简洁,但除非你 100% 确定想支持的浏览器在你想支持的平台下都能支持这个写法,否则还是不应该轻易用。

CSS3 新增的 @font-face 规则则是对于现有规则的扩展,提供了 web fonts 功能,但字体匹配算法的逻辑并没有改变,详细的算法可以看 CSS 规范里的说明。

当确定了字体以后,就可以将文本、字体等等参数一起交给具体的排版引擎,生成字形和位置,然后根据不同的平台调用不同的字体 rasterizer 将字形转换成最后显示在屏幕上的图案,一般浏览器都会选择平台原生的 rasterizer,比如 Mac OS X 下用 Core Graphics,Linux/X11 下用 FreeType,Windows 下用 GDI/DirectWrite 等等。关于这个步骤,typekit 的这篇 blog 可以作为参考。各个浏览器的差异主要来自使用的排版引擎可能对不同的语言支持有差异,调用 rasterizer 使用的参数可能有差异 (比如是否启用 subpixel rendering、使用的 hinting 级别等等),但在同一个操作系统下的效果差别不会很大。

基于以上的介绍,可以尝试提出一个在现有浏览器下,针对中文用户的,书写 CSS 字体选择规则的建议,如下:

  1. 首先确定要选择字体的元素应该使用的字体风格,比如是衬线字体、非衬线字体还是 cursive、fantasy 之类的;
  2. 确定了风格之后,先选择西文字体,优先把平台独特的、在该平台下效果更好的字体写上,比如 Mac OS X 下有 Helvetica 也有 Arial,但 Helvetica (可能) 效果更好,Windows 下则一般只有 Arial,那么写 Helvetica, Arial 就比 Arial, Helvetica 或者只有 Arial 更好;
  3. 然后列出中文字体,原则相同,多个平台共有的字体应该尽量放在后边,独有的字体放在前面,还需要照顾到 Mac OS X/Linux 下一般用户习惯用(细)黑体作为默认字体,Windows 下习惯以宋体作为默认字体的情况,比如 STXihei, SimSun 这样的写法比较常见,如果写作 SimSun, STXihei,但 Mac OS X 上装了 SimSun 效果就不会太好看。
  4. 最后还是应该放上对应的 generic family,比如 sans-serif 或者 serif。
  5. 尽量用字体的基本名称 (比如 English locale 下显示的),而不要用本地化过的名称。除非特殊情况 (Windows 下“某些”浏览器在特定编码下只能支持本地化的字体名称)。Mac OS X 下字体名称可以用 Font Book 查到 (菜单 Preview -> Show Font Info),Windows 下字体信息在微软的网站可以得到,Linux/X11 下可以使用 fc-list 命令查到。
  6. 字体名称中包含空格时记住用引号扩起,比如 “American Typewritter” 和 “Myriad Pro”。
  7. 文档开头最好指明语言,比如 <html lang=”zh-CN”>,可以使用的语言标记参见 W3C 的说明。

原文链接:http://www.wufangbo.com/browser-render-text/

2、关于提高浏览器渲染页面速度的建议

怎样尽可能的缩短浏览器上页面渲染的时间,文章从以下几方面着手:

  • 写出高效的css代码
  • 避免使用css表达式
  • 把css文件放在页面顶部
  • 指定页面图片的尺寸
  • 页面头部标明文档编码

一、写出高效的css代码

首先弄清浏览器解析html代码的过程:构建一个dom树,页面要显示的各元素都会创建到这个dom树当中。每当一个新元素加入到这个dom树当中,浏览器便会通过css引擎查遍css样式表,找到符合该元素的样式规则应用到这个元素上。css引擎查找样式表,对每条规则都按从右到左的顺序去匹配。

了解过程后,我们可以看出可以从两方面优化我们的css代码:1,定义的css样式规则条数越少越好,所以赶紧删除css文件中不必要的样式定 义;2,优化每条规则的选择符书写方式,尽量让css引擎一看就知道这个规则是否需要应用到当前这个元素上,让引擎少走不必要的弯路。

如以下几种效率不高的css书写方式:

a. 用通配符作为关键选择符(关键选择符指的是每条规则最右侧的选择符)

b. 用标签做关键选择符

c. 画蛇添足的写法

d. 给非连接标签添加 :hover 伪类,这会对用了strict doctype的页面在IE7和IE8下变的很慢。

优化建议:

  1. 避免使用通配符
  2. 让css引擎快速辨别该规则是否适用于当前元素:多用id或class选择符,少用标签选择符;
  3. 不要画蛇添足把id和class或标签和class等连着写;
  4. 尽量避免使用后代选择符,去除不必要的祖先元素,可以考虑使用class选择符来替换后代选择符;
  5. 避免给非连接标签添加 :hover 伪类。
    1. 二、避免使用css表达式

      css表达式仅在ie浏览器下才起作用,微软已在ie8后不推荐使用,因为它会严重影响页面性能:任何时候,不管任何一个事件被触发,例如窗口的 resize 事件,鼠标的移动等等,css表达式都会重新计算一遍。

      三、把css文件放在页面顶部

      把外联或内联样式表放在body部分会影响页面渲染的速度,因为浏览器只有在所有样式表下载完成后才会继续下载页面其他内容。另外,内联样式表(放在<style>内的样式)有可能会引起页面重新渲染或显示隐藏页面中的某些元素,建议不要使用内联样式表。

      四、指定页面图片的尺寸

      指定页面图片尺寸,要符合图片的真实尺寸(不要通过指定尺寸来缩放图片),可以避免尺寸改变导致的页面结构效果的变化,所以对加快页面渲染速度有益。

      五、页面头部标明文档编码

      HTML文档是以包含文档编码信息的数据流方式在网络间传输。页面的编码信息一般会在HTTP响应的头部信息或在文档内的HTML标记中指明。客户端浏览器只有在确定了页面编码后才能正确的渲染页面, 所以在绘制页面或执行任何的javascript代码前,大部分的浏览器(ie6、ie7、ie8除外)都会缓冲一定字节的数据来从中查找编码信息,不同 的浏览器当中预缓冲的字节数是不一样的。如果浏览器在接收到了设定的预缓冲数据量后还没有找到页面的编码信息,便会根据各自指定的默认编码开始渲染页面,如果这时再获取到页面编码信息,而又跟现在所用编码不一致,那整个页面就得重新渲染,某些情况下甚至需要重新获取数据。所以,对于大小超过1KB的页面(根据在各浏览器的测试情况,预缓冲数据量最多的也就1KB)应当尽早标明编码信息。

      优化建议:

      1. 尽量在HTTP头部信息中标明页面编码(这个需要在服务器端设置)。像Firefox浏览器,如果在HTTP头部信息就获取到了编码信息,便会预缓冲更少的数据从而减少不必要的数据缓冲时间;
      2. 在HTML的 <head> 部分标明编码信息;
      3. 要习惯给文档指定编码;
      4. 给页面指定的编码要符合页面实际编码;如果你在HTTP头部信息和HTML标记中同时指定了编码,需确保编码信息一致。

      原文链接:http://www.wufangbo.com/browser-page-rendering-speed/

      3、浏览器加载和渲染html的顺序

      1.浏览器加载和渲染html的顺序

      1. 浏览器加载和渲染html的顺序
      2. IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的。
      3. 在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元素都已经下载完)
      4. 如果遇到语义解释性的标签嵌入文件(JS脚本,CSS样式),那么此时IE的下载过程会启用单独连接进行下载。
      5. 并且在下载后进行解析,解析过程中,停止页面所有往下元素的下载。阻塞加载
      6. 样式表在下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行渲染。
      7. JS、CSS中如有重定义,后定义函数将覆盖前定义函数

      2. JS的加载

      1. 不能并行下载和解析(阻塞下载)
      2. 当引用了JS的时候,浏览器发送1个js request就会一直等待该request的返回。因为浏览器需要1个稳定的DOM树结构,而JS中很有可能有代码直接改变了DOM树结构,比如使用 document.write 或 appendChild,甚至是直接使用的location.href进行跳转,浏览器为了防止出现JS修改DOM树,需要重新构建DOM树的情况,所以 就会阻塞其他的下载和呈现.

      3.如何加快HTML页面加载速度

      1. 页面减肥。页面的肥瘦是影响加载速度最重要的因素删除不必要的空格、注释。将inline的script和css移到外部文件,可以使用HTML Tidy来给HTML减肥,还可以使用一些压缩工具来给JavaScript减肥
      2. 减少文件数量。减少页面上引用的文件数量可以减少HTTP连接数。许多JavaScript、CSS文件可以合并最好合并,人家财帮子都把自己的JavaScript. functions和Prototype.js合并到一个base.js文件里去了
      3. 减少域名查询。DNS查询和解析域名也是消耗时间的,所以要减少对外部JavaScript、CSS、图片等资源的引用,不同域名的使用越少越好
      4. 缓存重用数据。使用缓存吧
      5. 优化页面元素加载顺序。首先加载页面最初显示的内容和与之相关的JavaScript和CSS,然后加载DHTML相关的东西,像什么不是最初显示相关的图片、flash、视频等很肥的资源就最后加载
      6. 减少inline JavaScript的数量。浏览器parser会假设inline JavaScript会改变页面结构,所以使用inline JavaScript开销较大,不要使用document.write()这种输出内容的方法,使用现代W3C DOM方法来为现代浏览器处理页面内容
      7. 使用现代CSS和合法的标签。使用现代CSS来减少标签和图像,例如使用现代CSS+文字完全可以替代一些只有文字的图片,使用合法的标签避免浏览器解析HTML时做“error correction”等操作,还可以被HTML Tidy来给HTML减肥
      8. Chunk your content。

        不要使用嵌套tables

        而使用非嵌套tables或者divs

      9. 指定图像和tables的大小。如果浏览器可以立即决定图像或tables的大小,那么它就可以马上显示页面而不要重新做一些布局安排的工作,这不仅加快了页面的显示,也预防了页面完成加载后布局的一些不当的改变。
      10. 根据用户浏览器明智的选择策略。IE、Firefox、Safari等等等等
      11. 页面结构的例子

      4.HTML页面加载和解析流程

      1. 用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件;
      2. 浏览器开始载入html代码,发现<head>标签内有一个<link>标签引用外部CSS文件;
      3. 浏览器又发出CSS文件的请求,服务器返回这个CSS文件;
      4. 浏览器继续载入html中<body>部分的代码,并且CSS文件已经拿到手了,可以开始渲染页面了;
      5. 浏览器在代码中发现一个<img>标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码;
      6. 服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码;
      7. 浏览器发现了一个包含一行Javascript代码的<script>标签,赶快运行它;
      8. Javascript脚本执行了这条语句,它命令浏览器隐藏掉代码中的某个<div> (style.display=”none”)。杯具啊,突然就少了这么一个元素,浏览器不得不重新渲染这部分代码;
      9. 终于等到了</html>的到来,浏览器泪流满面……
      10. 等等,还没完,用户点了一下界面中的“换肤”按钮,Javascript让浏览器换了一下<link>标签的CSS路径;
      11. 浏览器召集了在座的各位<div><span><ul><li>们,“大伙儿收拾收拾行李,咱得重新来过……”,浏览器向服务器请求了新的CSS文件,重新渲染页面。

      5.Yahoo对网页设计性能的建议,个人感觉是说得非常好的。

      • 英文版:http://developer.yahoo.com/performance/rules.html
      • 中文翻译:http://www.cnblogs.com/smjack/archive/2009/02/24/1396895.html

      原文链接:http://renyongjie668.blog.163.com/blog/static/1600531201097062789/

      4、拾人牙慧:不同浏览器如何渲染不同border-style值

      本文的一些测试结果不是出自我手,来自:How Do Browsers Render the Different CSS Border Style Values?

      以下是测试使用的代码:

      Value: dotted

      各个浏览器对比结果如下:

      dotted-border

      上面的对比结果直接把IE6给无视了(下同),唉,人家生活在American,国情不一样哈。IE7~IE8钻石形状,IE10全部可爱圆点,其他浏览器,自己看图吧。

      IE10的渲染已经接近规范的描述。圆角渲染最靠谱的应该是FireFox浏览器。

      Value: dashed

      dashed-border

      所有浏览器都是方条条,不过长度啊,空白间距啊都有挺标新立异的。

      根据规范定义,貌似哪个都对。如果您非要一大堆螃蟹里挑出个阳澄湖大闸蟹的话,FireFox那个丑陋的鸟样更准确些。

      Value: double

      double-b

      考验大家眼力的时候到了,找出其中不循规蹈矩的那一个。哦呵呵,恭喜你,找对了,就是IE10版本的边框,颜色淡了那么一点点。

      虽然真理往往掌握在少数人手中,但是,常识往往掌握在大多数人手中。我就不说什么了。

      Value: groove

      groove-border

      groove是凹槽的意思。不同浏览器下groove的长相就像女人一样,花样百变。我再一次就不说什么了。

      根据测试者的认识,没有一个是真正正确的,可能作者稍微抠了一点,颜色的变化应该是“曲线式”的,但是,上面没有一个是这样的。

      Value: ridge

      ridge-border

      ridge是山脊的意思。从效果来看,ridgegroove真称得上“奸夫淫妇”的美名哈。

      虽然长相不一,其实大家都是正确的。只不过都不是100% OK的那种。Firefox, Safari, 和Chrome应该是最准确的。

      Value: inset

      inset-border

      IE7和IE8的内陷的阴影真是好阴暗哦,果然应了那句话,相由心生。

      除了IE7和IE8,其他浏览器的边框都是可以竖大拇指滴赞

      Value: outset

      outset-border

      IE好像都蛮黑的。但是,基本上还都过得去。Safari和Chrome的相貌是最好的。

      不管你信不信,反正我是信了。IE7和IE8要比其他的来的更准确。恩恩,其他浏览器最大的问题在于就像个小胸的美女,难以区分前胸和后背。”inset”和”outset”长得过于类似。IE10尤其是个平胸女王,很容易让男朋友以为晚上都是趴着睡的。

      原文链接:http://itindex.net/detail/25308-%E6%B5%8F%E8%A7%88%E5%99%A8-%E6%B8%B2%E6%9F%93-border

      5、css的效率和浏览器渲染的速度

      浏览器如何读取你的CSS选择器有一个很重要的原则,那就是它们从右到左读取。这意味这像 ul > li a[title=”home”] 这样的选择器,a[title=”home”] 将是最先被读取的。

      我承认我并不经常想这个问题……我们写的css的效率是怎么样的呢,浏览器渲染的速度又如何呢?

      这是应该是浏览器开发者应该关心的(页面加载更快,用户就会更愉快)。Mozilla有一篇文章: about best practices . Google 当然也很关心这个问题,他们也有这样一篇文章:Optimize browser rendering

      让我们了解下他们主要倡导的东东,然后讨论他们的实用性。

      从右到左

      浏览器如何读取你的CSS选择器有一个很重要的原则,那就是它们从右到左读取。这意味这像 ul > li a[title=”home”] 这样的选择器,a[title=”home”] 将是最先被读取的。这一部分通常被称为 “key selector” (可否称为“目标选择器” -_-!)选择器的最后一部分,也是被选择的标签。

      ID’s 是最有效率的,通用符是最慢的

      有四种目标选择器:ID, class, tag和通用符。看下他们各自的效率如何:

      尽管这让人有点费解……因为ID’s是最高效的,浏览器可以通过ID迅速的找到 li。但事实是,li 标签是被先读取的。

      不要用标签修饰

      死也不要像下面这样干:

      ID’s 是唯一的,所以不需要用标签修饰,这只会让它更低效。

      如果你可以避免的话,也不要用它修饰 class 。class 不是唯一的,所以理论上你可以把它用在不同的标签。如果你愿意的话,你可以用标签控制不同的样式,这样你可能需要标签修饰(比如:li.first),但这样做的人很少,所以,don’t .

      绝对没有比用后代选择器更糟糕的做法了

      David Hyatt:
      后代选择器是CSS里最昂贵的选择器,昂贵得可怕——特别是当它放在标签和通用符后面时。

      就如下面这个东东一样,绝对的效率毒瘤:

      一个选择器渲染失败比这个选择器被渲染更高效

      我不是很确定是否有更好的证据去证明这一点,因为如果你有大量的选择器在CSS样式表里无法找到,这样的事情貌似很离奇,但一点必需注意的是,从右到左的解释一个选择器来说,一旦它找不到,那它就会停止尝试。然而如果它找到了,那它就需要花更多精力去解释了。

      试想一下为何你这样写选择器

      思考下这东东:

      你可能不需要从 a 选择器开始(如果你只是想换个字体)。下面这个可能更高效些:

      实用性

      还刻前面提到的Mozilla的一篇文章?已经有十年了。事实是:计算机比十年前变慢了(不是我理解错了,还是作者想说现在的WEB越来越复杂了)。我感觉这东东在当年似乎还更受重视。十年前我还是个21岁的英俊小生,当然我不觉得那里我会认识css这东东。所以我也无法跟你讲以前的情况……但我觉得渲染效率的问题之所以没被重视是因为这从来就不是一个大问题。

      这是我的一些看法:不管怎样上面提到的东东都是有意义的,你可以按照上面的方法去做,因为它并不会限制你的CSS制作。但你也没必要太教条主义。如果你是个完美主义者,而之前又没有考虑过那东东,那是时候去重新看一下你之前写的一些样式是否有改进的地方了。如果你没发现你的网站明显的渲染缓慢,那大可别太在意,在以后的工作中多注意就行了。

      超级快速,零实用性

      我们知道ID’s 是最高效的选择器。当你想让渲染速度最高效时,你可能会给每个独立的标签配置一个ID,然后用这些ID写样式。那会超级快,也超级荒唐。这样的结果是语义极差,维护难到了极点。即使在核心部分你也不应该见过这样做的。我认为这个可以提醒我们不要为了高效的CSS放弃语义和可维护性。

      Thanks to Jason Beaudoin for emailing me about the idea. If anyone knows more about this stuff, or if you have additional tips that you use in this same vein, let’s hear it!

      顺便提一下,因为CSS选择器被很多javascript库使用,上面提到的东东仍然适用,ID选择器还是最快的,后代选择器和类似的东东比较慢。

      PS:看谁还敢用N多的后代选择器。还有反对我用ID的。哇哈哈。

      原文链接:http://www.enet.com.cn/article/2010/0528/A20100528661762.shtml

      6、浏览器的重绘repaints与重排reflows深入分析

      在项目的交互或视觉评审中,前端同学常常会对一些交互效果质疑,提出这样做不好那样做不好。主要原因是这些效果通常会产生一系列的浏览器重绘 (redraw)和重排(reflow),需要付出高昂的性能代价。那么,什么是浏览器的重绘和重排呢?二者何时发生以及如何权衡?如何在具体的开发过程 中将重绘和重排引发的性能问题考虑进去?本文期待可以部分解释以上三个问题。

      浏览器从下载文档到显示页面的过程是个复杂的过程,这里包含了重绘和重排。各家浏览器引擎的工作原理略有差别,但也有一定规则。简单讲,通常在文档 初次加载时,浏览器引擎会解析HTML文档来构建DOM树,之后根据DOM元素的几何属性构建一棵用于渲染的树。渲染树的每个节点都有大小和边距等属性, 类似于盒子模型(由于隐藏元素不需要显示,渲染树中并不包含DOM树中隐藏的元素)。当渲染树构建完成后,浏览器就可以将元素放置到正确的位置了,再根据 渲染树节点的样式属性绘制出页面。由于浏览器的流布局,对渲染树的计算通常只需要遍历一次就可以完成。但table及其内部元素除外,它可能需要多次计算 才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。这也是为什么我们要避免使用table做布局的一个原因。

      重绘是一个元素外观的改变所触发的浏览器行为,例如改变visibility、outline、背景色等属性。浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。重绘不会带来重新布局,并不一定伴随重排。

      重排是更明显的一种改变,可以理解为渲染树需要重新计算。下面是常见的触发重排的操作:

      1. DOM元素的几何属性变化

      当DOM元素的几何属性变化时,渲染树中的相关节点就会失效,浏览器会根据DOM元素的变化重新构建渲染树中失效的节点。之后,会根据新的渲染树重 新绘制这部分页面。而且,当前元素的重排也许会带来相关元素的重排。例如,容器节点的渲染树改变时,会触发子节点的重新计算,也会触发其后续兄弟节点的重 排,祖先节点需要重新计算子节点的尺寸也会产生重排。最后,每个元素都将发生重绘。可见,重排一定会引起浏览器的重绘,一个元素的重排通常会带来一系列的 反应,甚至触发整个文档的重排和重绘,性能代价是高昂的。

      2. DOM树的结构变化

      当DOM树的结构变化时,例如节点的增减、移动等,也会触发重排。浏览器引擎布局的过程,类似于树的前序遍历,是一个从上到下从左到右的过程。通常 在这个过程中,当前元素不会再影响其前面已经遍历过的元素。所以,如果在body最前面插入一个元素,会导致整个文档的重新渲染,而在其后插入一个元素, 则不会影响到前面的元素。

      3. 获取某些属性

      浏览器引擎可能会针对重排做了优化。比如Opera,它会等到有足够数量的变化发生,或者等到一定的时间,或者等一个线程结束,再一起处理,这样就 只发生一次重排。但除了渲染树的直接变化,当获取一些属性时,浏览器为取得正确的值也会触发重排。这样就使得浏览器的优化失效了。这些属性包 括:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、 clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle() (currentStyle in IE)。所以,在多次使用这些值时应进行缓存。

      此外,改变元素的一些样式,调整浏览器窗口大小等等也都将触发重排。

      开发中,比较好的实践是尽量减少重排次数和缩小重排的影响范围。例如:

      1. 将多次改变样式属性的操作合并成一次操作。例如,

        JS:

        可以合并为:

        CSS:

        JS:

      2. 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位。
      3. 在内存中多次操作节点,完成后再添加到文档中去。例如要异步获取表格数据,渲染到页面。可以先取得数据后在内存中构建整个表格的html片段,再一次性添加到文档中去,而不是循环添加每一行。
      4. 由于display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发2次重排。
      5. 在需要经常获取那些引起浏览器重排的属性值时,要缓存到变量。

      在最近几次面试中比较常问的一个问题:在前端如何实现一个表格的排序。如果应聘者的方案中考虑到了如何减少重绘和重排的影响,将是使人满意的方案。

      参考文档:

      Loading Web pages

      http://www.whatwg.org/specs/web-apps/current-work/multipage/browsers.html#browsers

      Rendering

      http://www.whatwg.org/specs/web-apps/current-work/multipage/rendering.html#rendering

      WebCore RenderingI – The Basics

      http://www.webkit.org/blog/114/webcore-rendering-i-the-basics/

      Notes on HTML Reflow

      http://www-archive.mozilla.org/newlayout/doc/reflow.html

      7、浏览器控件的重绘问题

      在有些情况下,窗口内嵌的浏览器控件不能及时重绘。例如,我们建立一个”消息提示窗”,采用浏览器控件显示消息的内容。当我们采用AnimateWindow进行动画显示时,浏览器控件会显示一片空白。下面我们探讨浏览器控件的重绘问题。

      1.     重现场景。
      2. 重绘的目标。

        一个浏览器控件的窗口层次如下:

        我们需要重绘的目标是类名为“Internet Explorer_Server”的窗口句柄。为了支持最新的vista系统,不能简单地通过IWebBrowser2->get_HWND取得窗口句柄。下面,我们通过AtlAxCreateControlEx返回的pUnkControl,取得“Shell Embedding”,再取得“Internet Explorer_Server”。

        取得“Shell Embedding”:

        取得“Internet Explorer_Server”:

      3. 重绘的时机。

        窗口动画将执行一段指定的时间。用户要看到的是最后的页面内容,因此不必在动画过程中启动重绘,而是在动画完成之后进行。那么怎样才知道AnimateWindow结束了呢?幸运的是,AnimateWindow采用的是同步返回(与AJAX中流行的异步调用思想是不同的。)。也就是说,当动画执行完毕后,AnimateWindow才返回。因此,重绘的时机是在AnimateWindow之后。

       

      原文链接:http://blog.csdn.net/pimshell/article/details/1611199

分享此文到:

本文出自 空气的时光记事本,非注明转载皆为原创,转载时请注明出处及相应链接。

本文永久链接: http://www.liujingze.com/%e6%b5%8f%e8%a7%88%e5%99%a8%ef%bc%9a%e6%b8%b2%e6%9f%93%e3%80%81%e9%87%8d%e7%bb%98%e3%80%81%e9%87%8d%e6%8e%92%e4%b8%a4%e4%b8%89%e4%ba%8b.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*