一年前开始看这本书,然而书里的划线笔记,现在基本想不起来,估计那时候是非常一知半解。有了一年多的项目经验后,再次看这本书,并整理了些工作中遇到过或我认为会有用的东西
加载和执行
- 将脚本放在文档底部加载。避免浏览器在下载解析和执行js代码时的等待。
- 合并Js代码。HTTP请求会带来额外的性能开销。内嵌脚本也需要合并
- 无阻塞脚本。文件的下载和执行过程不会阻塞页面其他进程。
- defer属性。 可与其他资源并行下载,待DOM加载完成后才会执行。
- Async 属性。支持包含src的元素,并行下载,下载完成后自动执行。无法控制先后顺序。
- 动态资源加载。文件在<script>元素被添加到页面时开始下载。通常下载后会立即执行。
推荐无阻塞模式
先添加动态加载所需的代码,然后加载初始化页面所需的剩余代码。
function loadScript(url, fallback){
var script = document.createElement("script")
script.type = "text/javascript"
script.onload = function() {
callback()
}
}
数据存取
- 函数中读取局部变量总是最快的,而读取全局变量则是最快的,全局变量总是在执行环境作用域链的顶端。
- 若某个跨作用域的值在函数中被引用了一次以上,则可以把它存储到局部变量里。
闭包、作用域和内存
函数的活动对象会随着执行环境一同销毁。但引入闭包后,激活对象无法被销毁。闭包比非闭包函数相比,需要更多点额内存开销。
嵌套成员
- 对象成员嵌套越深,读取速度就会越慢,执行location.href 比 window.location.href 要快
- 在函数中读取同一个对象属性,最佳做法是将属性值保存到局部变量中。
DOM编程
- 两个相互独立的功能(如DOM和Js)只要通过接口彼此连接,就会产生消耗
- 修改元素会导致重绘和重排。
- 合并HTML的innerHTML 比 DOM标准方法,通常性能更好
- 选择器API。document.querrySelectorAll("#menu a") 更高效
浏览器下载完页面中所有组件——HTML标记、JavaScript、CSS、图片——之后会生成两个内部数据结构:
- DOM树。表示页面结构
- 渲染树。表示DOM节点如何显示
DOM的变化影响元素的几何属性(宽、高), 浏览器需要重新计算几何属性,其他元素的几何属性和位置也会受到影响。
- 重排。页面布局和几何属性改变时需要“重排”。浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。
- 重绘。完成重排后,浏览器会重新绘制受影响的部分到屏幕中。
减少重绘与重排
- 批量操作DOM
- 缓存布局信息。同函数局部缓存
- 让元素脱离动画流。使用绝对定位
- 事件委托。事件逐层并能被父元素捕获,只需给外层父元素绑定一个处理器,即可处理其子元素上的所有事件。
算法和流程控制
代码的组织结构和解决具体问题的思路是影响代码性能的主要因素
代码执行时间大部分消耗在循环中
循环类型
- For 循环。初始化、前测条件、循环体、后执行代码组成
- While 循环。前测条件和循环体构成
- do-While 循环。循环体和后测条件构成,至少发生一次
- for-in 循环。可以枚举对象的属性名。每次迭代会同时搜寻原型和实例属性,应避免使用。
循环优化
- 改善循环性能的最佳方式是减少每次迭代的运算量和减少循环迭代次数
- 通常来说,对离散点来说,switch 比 if-else 快,判断条件多时使用查找表
- 使用Memorization 来避免重复计算
快速响应用户界面
浏览器UI线程,大多数浏览器让一个单线程共用于执行JavaScript 和更新用户界面
- 任何JavaScript 任务都不应该执行超过100ms, 过长会导致 UI 更新出现明显延迟
- 定时器可以用来安排代码延迟执行,使得可以把长时间运行的脚本分解成一系列小任务
- Web Workers,允许在UI线程外部执行 JavaScript 代码,避免锁定UI
Ajax
- 减少请求数,可通过合并 Javascript 和 CSS 文件
- 缩短页面加载时间,页面主要内容加载后,使用 Ajax 获取那些次要文件
- 缓存数据,设置HTTP头部的文件过期时间
编程实践
- 使用数组或对象直接量创建
- 延迟加载,第一次加载时去检测,之后用新函数覆盖旧检测函数,避免重复检测
- 条件预加载。脚本加载期间提前检测
构建并部署高性能的 JavaScript 应用
- 合并、预处理、压缩 文件
- 缓存文件,当应用升级时,用时间戳重命名,确保下载最新的
- 使用内容分发网络