将样式表放在顶部
把你的样式放在 head 标签中。
不要使用 @import
加载你的样式表,它会在最后加载导致页面出现白屏现象。
将脚本放在底部
脚本下载时并行下载禁用,因为脚本可能使用 document.write 来修改页面内容,也是为了保证脚本能够按照正确的顺序执行。
避免 CSS 表达式
CSS Expression,动态 CSS 属性,IE 私有,自 5.0 开始引入(IE8 将不再支持)不要使用
优点
- 使 CSS 属性动态生成,所以基本 js 能干的它都能干
- 使用 CSS 选择符,比 js 遍历到某个特定元素要方便得多
缺点
expression 会反复执行,有严重的效率问题。它的触发似乎不是通过事件,而是通过 interval 一类的机制。
别的浏览器不支持,IE8 也将不再支持
使用外部 JavaScript 和 CSS
使用外部 JavaScript 和 CSS 有机会被浏览器缓存起来
减少 DNS 查找
DNS 缓存会影响服务器的性能,你需要设置合适的 TTL 的值。减少唯一主机名会减少 DNS 查找的数量,但是会减少并行下载的数量。所以使用 2-4 个主机名是比较合适的做法。使用 Keep-Alive
重用现有连接也会减少 DNS 的查找。
DNS 预解析
<link rel="dns-prefetch" href="//libs.cdnjs.net">
<link rel="dns-prefetch" href="//tjs.sjs.sinajs.cn">
<link rel="dns-prefetch" href="//blog-1257169527.file.myqcloud.com">
引擎渲染和浏览器
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="renderer" content="webkit">
精简 JavaScript
精简
从代码中移除不必要的字符减少其大小,进而改善加载时间的实践。在代码被精简后,所有的注释和不必要的空白字符都被移除。对于 CSS 使用 0 代替 0px,使用 #606 代替 #660066。
混淆
移除空白和注释,同时改写代码。作为改写的一部分,函数和变量的名称都会被替换为更短的字符串,这时代码更加精简也更难阅读。这样做是为了增加反向工程的难度,对提高性能也有帮助。
缺陷
由于混淆更为复杂,混淆过程中可能引入错误。
维护
由于混淆会改变 JavaScript 符号,因此需要对任何不能改变的符号进行标记,防止混淆时修改它们。
调试
混淆的代码难以阅读,调试变得困难。
理解 Ajax 性能
减少迭代的开销、次数或者重新设计应用程序。减少循环的嵌套或者更换算法。
浏览器通常在 JavaScript 上花费的时间很少,绝大多数时间都消耗在 DOM 上。
创建快速响应的 Web 应用
怎么才算快
- 0.1 秒:用直接操作 UI 中的对象的感觉极限。
- 1 秒:用户随意地在计算机指令空间进行操作而无需过度等待的感觉极限。超过 1 秒但不到 10 秒的延时要提示用户计算机正在解决这个问题。
- 10 秒:用户专注于任务的极限。超过 10 秒的任何操作需要一个百分比完成进度的指示器。
确保响应速度
一旦确定代码的性能不好并且无法优化它时,可以使用 Web Workers。
拆分初始化负载
将要下载的 JavaScript 拆分为两个部分,一部分用于页面初始化,另一部分则可以延时加载。
无阻塞加载脚本
Script DOM Element
var sriptElem = document.createElement('script');
scriptElem.src = 'htttp://anydomain.com/A.js';
document.getElementByName('head')[0].appendChlid(scriptElem);
Script Defer
下载设置 defer 属性的脚本时,允许其它脚本下载。
<script defer src="htttp://anydomain.com/A.js"></script>
Script Async
对于没有任何依赖的 JS 文件可以加上 async
属性,表示 JS 文件下载和解析不会阻塞渲染。
整合异步代码
异步代码可能会和行内代码之间存在代码依赖关系。JavaScript 下载完成的时间不确定,可能造成出现依赖代码没有加载完成,出现竞争态。异步加载代码保持执行顺序的最佳方式是使用 Script Onload。行内代码可以在外部代码加载完成后立即执行。
布置行内脚本
避免行内代码阻塞并行下载的方案:
- 把行内代码移至底部。
- 使用异步回调启动 JavaScript 的执行。
- 使用 script 的 defer 属性。
样式表后面的行内脚本会阻止所有后续资源的下载。
编写高效的 JavaScript
管理作用域
标识符在作用域链中查找和访问它所需的时间长,会给脚本的执行时间带来负面影响。
使用局部变量
- 局部变量是 JavaScript 读写最快的标识符。
- 任何非局部变量在函数中使用超过一次,都应该将其储存为局部变量。
- 全局变量始终是作用域中最后一个对象,所以全局标识符的解析总是最耗时的。
高效的数据读取
脚本有 4 种地方可以读取数据:
- 字面量值
- 变量
- 数组元素
- 对象属性
流控制
快速条件判断
- 使用 if 语句
- 一两个判断。
- 大量的值能很容易区分到不同的区间。
- 使用 switch 语句
- 超过两个而少于 10 个离散值需要判断。
- 条件值是非线性的,无法分离出区间范围。
- 使用数组查询
- 超过 10 个值需要判断
- 条件对应的结果是单一的,而不是一系列操作。
快速循环
- 使用
var length = values.length
代替values.length
可以避免每次都进行属性查找。 - 避免 for-in 循环
避免运行时间过长的脚本
一般而言脚本执行不能超过 100 毫秒。常见的脚本执行时间过长的原因包括:
- 过多的 DOM 交互
- 过多的循环
- 过多的递归
使用定时器挂起
当脚本运行需要花太长时间运行的时候,就让部分延时执行。过小的延时会引起浏览器不响应,一般而言延时 50-100 毫秒是合适的,这足够让浏览器有时间执行必要的页面更新。
参考
【1】高性能网站建设进阶指南:Web 开发者性能优化最佳实践
【2】高性能网站建设指南:前端工程师技能精髓