前端性能优化准则

performance

从客户端性能、服务器端、网络性能考虑。收录的主要是现阶段能使用,并且可以理解运用的部分。

1. 页面内容

1.1 减少HTTP请求数

前端 80% 的响应时间都消耗在图片、样式、脚本等资源加载上。浏览器对每个域名的连接数有限制,所以减少请求数是缩短响应时间的关键。

  • 合并 JavaScript、CSS 等文件

    • 服务器端(CDN)自动合并
    • 基于Node.js的合并工具
  • 使用CSS Sprite:将背景图片合并成一个文件

未来的趋势是使用内嵌 SVG(暂时还未使用过)。

1.2 减少 DNS 查询

用户输入 URL 以后,浏览器首先要查询域名(hostname)对应服务器的 IP 地址,一般需要耗费 20-120 毫秒 时间。DNS 查询完成之前,浏览器无法从服务器下载任何数据。

首次访问、没有相应的 DNS 缓存时,域名越多,查询时间越长。所以应尽量减少域名数量。但基于并行下载考虑,把资源分布到 2 个域名上(最多不超过 4 个)。这是减少 DNS 查询同时保证并行下载的折衷方案。

1.3 延迟加载

页面初始加载时哪些内容是绝对必需的?不在答案之列的资源都可以延迟加载。比如:

  • 非首屏使用的数据、样式、脚本、图片等;
  • 用户交互时才会显示的内容。

遵循「渐进增强」理念开发的网站:JavaScript 用于增强用用户体验,但没有(不支持) JavaScript 也能正常工作,完全可以延迟加载 JavaScript。

1.4 预先加载

预先加载利用浏览器空闲时间请求将来要使用的资源,以便用户访问下一页面时更快地响应。

  • 无条件预先加载:页面加载完成(load)后,马上获取其他资源。以 google.com 为例,首页加载完成后会立即下载一个 Sprite 图片,此图首页不需要,但是搜索结果页要用到。
  • 有条件预先加载:根据用户行为预判用户去向,预载相关资源。比如 search.yahoo.com 开始输入时会有额外的资源加载。
  • 有「阴谋」的预先加载:页面即将上线新版前预先加载新版内容。网站改版后由于缓存、使用习惯等原因,会有旧版的网站更快更流畅的反馈。 为缓解这一问题,在新版上线之前,旧版可以利用空闲提前加载一些新版的资源缓存到客户端,以便新版正式上线后更快的载入。

「双十一」这类促销日来临之前,也可以预先下载一些相关资源到客户端(浏览器、App 等),有效利用浏览器缓存和本地存储,降低活动当日请求压力,提高用户体验。

1.5 减少 DOM 元素数量

从以下几个角度考虑移除不必要的标记:

  • 是否还在使用表格布局?
  • 塞进去更多的
    仅为了处理布局问题?也许有更好、更语义化的标记。
  • 能通过伪元素实现的功能,就没必要添加额外元素,如清除浮动。

浏览器控制台中输入以下代码可以计算出页面中有多少 DOM 元素:

1
document.getElementsByTagName('*').length;

为什么不使用表格布局?

  • 更多的标签,增加文件大小;
  • 不易维护,无法适应响应式设计;
  • 性能考量,默认的表格布局算法会产生大量重绘

1.6 划分内容到不同域名

浏览器一般会限制每个域的并行线程(一般为 6 个,甚至更少),使用不同的域名可以最大化下载线程,但注意保持在 2-4 个域名内,以避免 DNS 查询损耗。

例如,动态内容放在csspod.com上,静态资源放在static.csspod.com上。这样还可以禁用静态资源域下的 Cookie,减少数据传输,静态资源一般无需使用 Cookie,可以把它们放在使用二级域名或者专门域名的无 Cookie 服务器上,降低 Cookie 传送的造成的流量浪费,提高响应速度。

1.7 尽量减少 iframe 使用

iframe优点

  • 可以用来加载速度较慢的第三方资源,如广告、徽章;
  • 可以并行下载脚本。

iframe缺点

  • 加载代价昂贵,即使是空的页面;
  • 阻塞页面load事件触发;

Iframe 完全加载以后,父页面才会触发load事件。Safari、Chrome 中通过 JavaScript 动态设置iframe src可以避免这个问题。

  • 缺乏语义;

1.8 避免 404 错误

返回无效的响应(如 404 未找到)完全没必要,降低用户体验而且毫无益处。

一些网站设计有提示信息的 404 页面,有助于提高用户体验,但还是浪费服务器资源。尤其糟糕的是外部脚本返回 404,不仅阻塞其他资源下载,浏览器还会尝试把 404 页面内容当作 JavaScript 解析,消耗更多资源。

1.9 定义字符集

放在head顶部。大多数浏览器会暂停页面渲染,直到找到字符集定义。

2.服务器

1.使用CDN

减少资源下载时间是性能优化的黄金法则。

2.Ajax 请求使用 GET 方法

浏览器执行 XMLHttpRequest POST 请求时分成两步,先发送 Header,再发送数据。而 GET 只使用一个 TCP 数据包发送数据,所以首选GET方法。

根据 HTTP 规范,GET 用于获取数据,POST 则用于向服务器发送数据,所以 Ajax 请求数据时使用 GET 更符合规范(GET 和 POST 对比)。

IE 中最大 URL 长度为 2K,如果超出 2K,则需要考虑使用POST方法。

3.避免图片 src 为空

图片src属性值为空字符串可能以下面两种形式出现:

1
2
3
4
5
/* HTML */
<img src="" />
/* JavaScript */
var img = new Image();
img.src = "";

虽然src属性为空字符串,但浏览器仍然会向服务器发起一个 HTTP 请求,空src产生请求的后果:

  • 给服务器造成意外的流量负担;
  • 浪费服务器计算资源;
  • 可能产生报错;

1.减少Cookie大小

Cookie 被用于身份认证、个性化设置等诸多用途。Cookie 通过 HTTP 头在服务器和浏览器间来回传送,减少 Cookie 大小可以降低其对响应速度的影响。

  • 去除不必要的 Cookie;
  • 尽量压缩 Cookie 大小;
  • 设置合适的过期时间。

4.CSS

4.1 把样式表放在中

样式表放在head中可以让页面渐进渲染,尽早呈现视觉反馈,给用户加载速度很快的感觉。

这对内容比较多的页面尤为重要,用户可以先查看已经下载渲染的内容,而不是盯着白屏等待。

4.2 不要使用 CSS 表达式

CSS 表达式可以在 CSS 里执行 JavaScript,仅 IE5-IE7 支持,IE8 标准模式已经废弃。

CSS 表达式超出预期的频繁执行,页面滚动、鼠标移动时都会不断执行,带来很大的性能损耗。

4.3 使用替代@import

对于 IE 某些版本,@import的行为和 link 放在页面底部一样。

5.JavaScript

5.1 把脚本放在页面底部

浏览器下载脚本时,会阻塞其他资源并行下载,即使是来自不同域名的资源。因此,最好将脚本放在底部,以提高页面加载速度。

一些特殊场景无法将脚本放到页面底部的,可以考虑 script 的以下属性:

  • defer属性;
  • HTML5 新增的 async属性;

5.2 使用外部 JavaScript 和 CSS

外部 JavaScript 和 CSS 文件可以被浏览器缓存,在不同页面间重用,也能降低页面大小。另外,可以在首页加载完成以后,预先加载子页面的资源。

5.3 压缩 JavaScript 和 CSS

压缩代码可以移除非功能性的字符(注释、空格、空行等),减少文件大小,提高载入速度。

5.4 减少 DOM 操作

JavaScript 操作 DOM 很慢,尤其是 DOM 节点很多时。
使用时应该注意:

  • 操作 className,而不是多次读写style;
  • 避免使用 JavaScript 修复布局。

5.5 使用高效的事件处理

  • 减少绑定事件监听的节点,如通过事件委托;
  • 尽早处理事件,在DOMContentLoaded即可进行,不用等到load以后。

对于resize、scroll等触发频率极高的事件,应该通过 debounce 等机制降低处理程序执行频率。

6. 图片

6.1 优化 CSS Sprite

  • 水平排列 Sprite 中的图片,垂直排列会增加图片大小;
  • Spirite 中把颜色较近的组合在一起可以降低颜色数,理想状况是低于 256 色以适用 PNG8 格式;
  • 不要在 Spirite 的图像中间留有较大空隙。减少空隙虽然不太影响文件大小,但可以降低用户代理把图片解压为像素图的内存消耗,对移动设备更友好。

6.2 不要在 HTML 中缩放图片

不要使用HTML中的width、height缩放图片,如果用到小图片,就使用相应大小的图片。

6.2 图片相关补充

设置图片的宽和高,以免浏览器按照「猜」的宽高给图片保留的区域和实际宽高差异,产生重绘。