logo头像

叩首问路,码梦为生

前端面试系列(12)——前端性能优化

近几年的前端发展异常火爆,各种框架各种技术层出不穷,而所有的前端框架面临着同一个问题:怎样才能缩短首屏时间,让用户以最快的速度看到页面最想展现的内容;所以,前端性能优化这一问题成为所有前端开发人员需要考虑的需求。下面我将从 html、css、js 等方面介绍前端性能优化的一些方式,考虑到篇幅原因,只会以列表的形式进行简单概括,具体做法可以自行查阅,在这里强烈推荐雅虎军规,Yahoo Rules 是对于前端性能优化的全面总结,有时间一定要看一看

HTML 的优化

使用相对 URL

某些 href、src 属性如果与当前页面处于同一域名下,则使用相对 URL 能够节省至少一个域名的长度。

删除 HTTP 或者 HTTPS

绝对 URL 都以 HTTP 或 HTTPS 等协议头开始,如果能确定 URL 的协议与当前页面 URL 的协议是一致的,或者说该 URL 在多种协议下均是可用的,则可以考虑删除这个协议头。

删除注释

考虑不必要的 IE 条件注释和 CDATA 注释及自定义注释。

压缩空白符

对于多数标签,可以通过删除多余的空白符来减少 HTML 体积,但是对于 pre 等是例外。

压缩 inline css & Javascript

不管 inline 还是 external,都需要压缩,这是减小体积的最直接的方式。

CSS & Javascript 尽量外链

不仅可以减少体积,还能够充分利用浏览器的缓存机制。

删除元素默认属性

在 HTML 规范中,很多 HTML 元素的属性是有默认值的,对于这些默认值可以抹去不写。

避免使用 Iframe

创建 iframe 元素的开销要比创建其他类型的 DOM 元素高 1~2 个数量级

避免空链接属性

可以看做是上面“删除元素默认属性”的特例,即使图片的地址为空,浏览器依旧会以默认的规则去请求空地址

避免节点深层级嵌套

由于浏览器构建 DOM 文档的机制,深层级嵌套的节点在初始化构建时往往需要更多的内存占用,并且在遍历节点时也会更慢些

避免 Table 布局

构建 table 的开销也很大

显示指定文档字符集

如果浏览器不能获知页面的编码字符集,一般都会在执行脚本和渲染页面前,把字节流缓存,然后再搜索可进行解析的字符集,或以默认的字符集来解析页面代码,这会导致消耗不必要的时间。为了避免浏览器把时间花费在搜寻合适的字符集来进行解码,所以最好在文档中总是显式的指定页面字符集。

CSS 的优化

把 CSS 放到代码页上端

加快渲染

避免 CSS 表达式

表达式的问题就在于它的计算频率要比我们想象的多。不仅仅是在页面显示和缩放时,就是在页面滚动、乃至移动鼠标时都会要重新计算一次。给 CSS 表达式增加一个计数器可以跟踪表达式的计算频率。在页面中随便移动鼠标都可以轻松达到 10000 次以上的计算量。

在外部的 CSS 文件中使用 @import 会使得页面在加载时增加额外的延迟。虽然规则允许在样式中调用 @import 来导入其它的 CSS,但浏览器不能并行下载样式,就会导致页面增添了额外的往返耗时。比如,第一个 CSS 文件 first.css 包含了以下内容:@import url(“second.css”)。那么浏览器就必须先把 first.css 下载、解析和执行后,才发现及处理第二个文件 second.css。

避免通配选择器

CSS 选择器对性能的影响源于浏览器匹配选择器和文档元素时所消耗的时间,所以优化选择器的原则是应尽量避免需要消耗更多匹配时间的选择器。比如这种反例:

1
.selected * {color: red;}

由于 CSS 选择器从右到左匹配的机制,浏览器匹配文档中所有的元素后分别向上逐级匹配 class 为 selected 的元素,直到文档的根节点,因此其匹配开销是非常大的,通常比开销最小的 ID 选择器高出 1~3 个数量级,所以应避免使用关键选择器是通配选择器的规则。

避免单规则的属性选择器

属性选择器根据元素的属性是否存在或其属性值进行匹配,如下例规则会把 herf 属性值等于 ”#index” 的链接元素设置为红色:

1
.selected [href=”#index”] {color: red;}

但其匹配开销是非常大的,浏览器先匹配所有的元素,检查其是否有 href 属性并且 herf 属性值等于 ”#index”, 然后分别向上逐级匹配 class 为 selected 的元素,直到文档的根节点。所以应避免使用关键选择器是单规则属性选择器的规则。

避免类正则的属性选择器

CSS3 添加了复杂的属性选择器,可以通过类正则表达式的方式对元素的属性值进行匹配。当然这些类型的选择器定是会影响性能的,正则表达式匹配会比基于类别的匹配会慢很多。大部分情况下我们应尽量避免使用 *=, |=, ^=, $=,和 ~= 语法的属性选择器。

JavaScript 的优化

脚本放到 HTML 代码页底部

加快渲染,当然如果你的 js 会影响 dom 建立,那放在哪都是一样的了

移除重复脚本

封装方法是一个前端开发人员最基本的技能

减少 DOM 访问

使用 JavaScript 访问 DOM 元素是比较慢的,因此为了提升性能,应该做到:

  • 缓存已经查询过的元素;
  • 线下更新完节点之后再将它们添加到文档树中;
  • 避免使用 JavaScript 来修改页面布局;

使用事件代理

有时候我们会感觉到页面反应迟钝,这是因为 DOM 树元素中附加了过多的事件句柄并且些事件句病被频繁地触发。这就是为什么说使用事件代理是一种好方法了。如果你在一个 div 中有 10 个按钮,你只需要在 div 上附加一次事件句柄就可以了,而不用去为每一个按钮增加一个句柄。事件冒泡时你可以捕捉到事件并判断出是哪个事件发出的。

缓存选择器查询结果

选择器查询是开销很大的方法。所以,使用选择器的次数应该越少越好,并且尽可能缓存选中的结果,便于以后反复使用。 不要使用:

1
2
jQuery('#top').find('p.classA');
jQuery('#top').find('p.classB');

而是使用:

1
2
3
var cached = jQuery('#top');
cached.find('p.classA');
cached.find('p.classB');

使用微类库

通常开发者都会使用 JavaScript 类库,如 jQuery、Mootools、YUI、Dojo 等,但是开发者往往只是使用 JavaScript 类库中的部分功能。为了更大的提升性能,应尽量避免使用这类大而全的类库,而是按需使用微类库来辅助开发。

扩展阅读

  • [Absolute HTML Compressor](http://www.alentum.com/ahc/ “Absolute HTML Compressor”):HTML 的优化工具,压缩效果比较理想
  • Even Faster Web Sites:关于前端优化的书,本人没看过,但是豆瓣评分 8.6 的它应该还不错吧

参考文章

支付宝打赏 微信打赏

听说赞过就能年薪百万