问题
- 访问和修改dom元素
- 修改dom元素的样式会导致重绘和重排
- 通过dom事件处理与用户的交互
dom,为什么慢
浏览器的dom和js
浏览器通常会把DOM和js独立实现 ie:被分离在mshtml.dll(内部成为Trident)和JScript.dll。 safari:被分离为webCode和独立的js引擎 chrome:被分离为webCode和jsv8引擎 fireFox:被分离为Gecko和js引擎 前者负责渲染,后者负责js特效部分 分离后两者的通信会导致销毁,从而影响性能。所以我们应该尽量减少通信。 减少dom的访问次数,把运算留在js端处理。
更新html
innerHTML 生成html ie更快,新版不明显。(使用字符串合并低,推荐使用数组合并。) 使用原生(document.createElement() 之类的api)谷歌浏览器内核的浏览器更快。
对性能有苛刻要求的操作更新一大段html推荐使用innerHTML
其他根据可读性,稳定性,团队习惯,代码风格来决定。
节点克隆
更有效率,但不明显。
html集合
html集已一种“假定实时态”实时存在。底层文档更新,它也会跟着更新。 html集合一直与文档保持链接,每次访问都会执行查询,导致低效。
读取一个集合的length比读取一个数组的length要慢很多。
小集合缓存length。大集合直接上。 额外的拷贝会带来性能消耗。 优化集合循环,使用局部变量。
获取dom元素
ie中,nextString() 比 childNodes()更快 其他浏览器几乎相等。
元素节点
循环中需要判断节点类型并过滤非元素节点,这些不是必要的dom操作。 有时候我们只需访问元素节点
使用元素节点api 替代 元素属性 例子:
属性名 | 替代的属性 |
---|
children | childNodes | childElementCount | childNodes.length | firstElement | firstChild |
ie只支持children。
选择api
多个选择,提高效率
let errs = document.querySelectorAll('div.warning, div.notice')
选择api的性能更好,推荐使用
重排重绘
浏览器下载资源后解析生成两个内部数据结构 DOM树:页面结构 渲染树:DOM节点如果显示
DOM树的节点和渲染树上的节点是一一对应的。(隐藏的dom元素是没有对应节点的) 盒/帧:渲染树节点
一旦dom树和渲染树构建完成,浏览器开始绘制页面元素
重排和重绘:当dom的变化影响了元素的几何属性(宽高),导致浏览器需要重新计算元素的几何属性 其他元素的几何属性和位置也因此受到影响。浏览器会使渲染树中受到影响的部分失效,并重构。这过程称为重排。 完成重排后,浏览器会重新绘制受到影响的部分到屏幕,这过程称为重绘
简而言之:影响几何属性即重排,不影响几何属性只影响颜色之类的即重绘。
重排:
- 增减可见的dom元素
- 元素位置改变
- 元素尺寸改变
- 内容改变
- 浏览器初始化
- 浏览器窗口尺寸改变
注意滚动条的出现
渲染树变化的排队与刷新
获取布局信息的操作会导致队列刷新
offsetTop,offsetLeft,offsetWidth,offsetHeight
scrollTop,scrollLeft,scrollWidth,scrollHeight
clientTop,clientLeft,clientWidth,clientHeight
getComputedStyle()
以上属性和方法需要返回最新的布局信息, 因此浏览器不得不执行渲染队列中的待处理变化并触发重排以返回正确的值。
总结:在修改样式的过程中,最好避免使用上面的属性。
最小化重绘和重排
- 合并多次对dom和样式的修改,减少发生次数。
- 使用class一次性修改样式
改变名称更清晰,易于维护,免除显示性代码。改变类时需要检查联级样式,会有轻微性能影响。
批量修改dom
- 使元素脱离文档流
- 对其应用多重改变
- 把元素带回文档流
三种方法使dom脱离文档流
- 隐藏元素,应用修改,重新显示。(使用:
display:none,display:block ) - 使用文档片段在当前dom之外构建子树再拷贝回文档(使用
document.createDocumentFragment() )【推荐】 - 将元素拷贝到一个脱离文档的节点中,修改副本后再替换原始元素(使用
oldEl.cloneNode(true),old.parentNode.replaceNode(clone, oldEl) )
缓存布局信息
将元素信息存到局部变量,不要修改中获取元素信息
让元素脱离动画流
展开/折叠用来做隐藏和显示效果。这会导致大规模的重排。 优化:
- 使用绝对位置定位页面上的元素,将其脱离文档流
- 让元素动起来。当它扩大时,临时覆盖部分页面。小区域重绘,而不是重排并重绘页面的大部分。
- 当动画结束时恢复定位,从而只会下移一次文档的其他元素。
IE和hover:
ie中使用hover会降低性能。元素很多时避免使用。
事件委托
多个元素绑定事件,绑定事件是有代价的。这需要访问和修改元素,还要给元素绑定事件, 浏览器最后要跟踪每个事件处理器,这占据了更大的内存,也花费了更多的时间。而且我们 不太可能每个元素的事件都触发到。
使用事件委托,内部原理涉及事件冒泡和事件代理。
事件:
- 捕获
- 到达目标
- 冒泡
ie不支持捕获,只能使用冒泡
跨浏览器兼容
- 访问事件对象,判断事件来源
- 取消文档树中的冒泡
- 阻止默认行为
总结:
- 减少dom访问
- 局部变量存储do信息
- 有复用时,缓存集合长度,拷贝集合到数组
- 使用更快的api
- 批量修改样式,离线操作dom树,使用缓存。
- 动画中使用绝对定位,使用拖放代理
- 使用事件委托减少事件处理器数量。
|