DOM编程与算法流程控制

DOM 编程

文档对象模型是独立于语言的,用于 XML 和 HTML 文档的程序接口。尽管 DOM 是个与语言无关的 API,但是他在浏览器中的接口是用 Javascript 实现的,所以 DOM 就成为了现在 Js 编码的重要部分。

浏览器通常会把 DOM 和 JS 独立实现,比如 IE 就把 JS 的实现名为 JScript,位于 jscript.dll 中;而 DOM 的实现在另一个库,名为 mshtml.dll。比如 Google 的就是是用 Webkit 的 WebCore 库来渲染页面,js 就是 V8..等等

所以天生就慢,因为两个独立的功能通过一处收费的桥相连,每次交互都得收费,所以应该尽量减少交互次数。

DOM 访问与修改

访问就已经得付出代价了,修改的代价更高,因为这得让浏览器重新计算页面的几何变化。如果是循环的修改的话代价更高。

我们应该减少修改的次数,比如先用一个变量存下来,一次进行修改。
  • innerHTML 与原生 DOM

对于修改页面区域,一直有使用非标准但是支持良好的 innerHTML 还是使用 createElement 这种原生的 DOM 方法的争论,其实答案是相差无几。在除了最新的 webkit 之外(新版的恰恰相反),innerHTML 会更快一些。

  • 节点克隆

在需要重复的时候,克隆已有的节点会比新建稍微快一点点。老的浏览器提升的效果一般,最新的 chrome 下,快个 5 倍吧。

  • HTML 集合

就是说 document.getElementsByName(),document.getElementsByClassName(),document.getElementsByTagName(),以及 document.images,document.links,document.forms 和 document.forms[0].elements 返回的是 HTML 的集合。这东西是个伪数组,只提供了 length,以及数字索引的访问,并没有数组的方法,当然我们可以用 apply 或者 call 来调用。

重要的是,html集合与文档一直保持着连接,每次访问他的值的时候,都会重复执行查询的过程,这才是低效之源。(而且遍历HTML集合并进行修改由于集合的动态性可能会产生代码逻辑的问题)

我们可以在数据量较小的情况下只需要用变量保存下 length 就行了,在大数据量的情况下,我们可以先把集合转化成数组在再进行遍历,因为数组的操作速度会比集合快很多。

  • 访问集合元素时使用局部变量的形式会好一点(其实这只是作用域链的优化,并不是 DOM 的优化)

  • 遍历 DOM

DOM 的 api 提供了大量的方式来获取元素。比如 childNodes,nextSibling,firstChild。(注意这些不区分元素节点以及其他类型的节点,比如注释和文本节点)。但是我们可以使用 children,firstElementChild 等来做(注意 IE6,7,8 只支持 children),这几个的属性会快一些,因为集合项更少。

  • 选择器 api

现在提供了一种 querySelectAll 的来得到数据集合,他的速度更快。效率更高(因为他返回的是 NodeLists,不实时响应,所以没有性能的问题),还有个 querySelector 方法。

重绘与重排

浏览器会解析生成两个内部数据结构:DOM 树(页面结构)与渲染树(表示 DOM 节点如何显示)

DOM 树中每个需要显示的节点在渲染树里至少存在一个对应的节点(隐藏的元素没有节点)。渲染树的节点成为帧或者盒。

当 DOM 变化改变了元素的宽或者高,浏览器需要重新计算元素的几何属性,其他的位置也会受到影响。浏览器会使渲染树中受影响的部分失效,并重新构造渲染树,这个过程为”重排”。完成重排,浏览器重新绘制受影响的部分到屏幕中,这个过程是“重绘”。并不是每次 DOM 改变都会影响几何特性。例如改变背景色不会影响宽高,只会触发一次重绘

重排何时发生

  • 添加或者删除可见的 DOM 元素
  • 元素位置改变
  • 元素尺寸改变(外边,内边,边框厚度,宽,高等属性)
  • 内容改变,比如文本或者图片被不同尺寸的图片改变
  • 页面渲染器初始化
  • 浏览器尺寸的改变

根据改变的范围和程度,渲染树或大或小的部分也需要重新的计算。有些改变会触发真个页面的重排,例如:当滚动条出现时。

渲染树变化的排队与刷新

浏览器大多会通过队列化修改并批量执行优化重排的过程。我们经常会不知不觉的强行刷新队列并要求计划任务立即执行。比如 offsetTop,scrollTop,clientTop,getComputedStyle(),这些个属性以及方法都会刷新渲染队列,即使在获取最近未改变的也会重算。

最小化重绘以及重排

我们应该合并多次对 DOM 和样式的修改,然后一次处理掉

  • 比如改变样式的时候,我们改变 style 属性的时候,可以用 cssText 一次性修改掉或者修改一下 class。这样只会触发一次重排。

批量操作的时候

批量的时候减少重绘和重排的次数,通过以下的步骤可以减去第二步环节的重排重绘。

  • 1.使元素脱离文档流
  • 2.对齐应用多重改变
  • 3.将元素带回文档中

脱离文档流的方法:

  • 隐藏元素,应用修改,重新显示
  • 使用文档片段(fragment)在 DOM 外创建子树,再添加回文档(最推荐的技术)
  • 将原始文档拷贝到一个脱离文档的节点,然后修改好了之后再替换。

使元素脱离动画流

比如我们用展开/折叠的方式来显示和隐藏时,会导致页面一些部分不断地被向下推,导致昂贵的大规模重排

  • 我们可以先用绝对定位定义页面上的动画元素,将其脱离文档流
  • 进行动画,虽然会遮住部分页面,但是不会重排并重绘大部分内容
  • 动画结束恢复定位,从而只会下移一次文档的其他元素

hover

IE7 之后有了 hover 特性,但是 hover 其实很影响页面的速度,尤其在大量元素的时候。

事件委托

这个其实很简单,就是绑在父元素上,然后在冒泡的时候被触发的时候检验一下 e.target 就行了。

算法和流程控制

代码的组织结构和解决具体问题的思路是影响代码性能的主要因素。

循环

其实四种循环中(for,while,do,for-in)除了 for-in 之外,其他循环类型的性能都差不多,深究选择哪个意义不大。应该根据需求而不是意义。

我们应该尽量减少迭代的工作量,比如缓存好 length,还有倒序迭代也会稍微快一点(价值不大)。

那种限制循环次数的 Duff 也能够起到一定的作用(但是这种提升只有在迭代量很大,比如大于 1000 的时候才会体现)。

基于函数的迭代,例如foreach,这种肯定是比 for 循环慢的,多的开销花在给数组项调用外部方法。

条件语句

其实也是老生常谈,种类不多的选用 if-else,种类多的用 switch,主要是为了代码更加直观。性能 switch 也会稍微好一点。

优化 if-else
  • 首先是可能性最大的放到最上面
  • 其次是我们有时在判断多个值域的时候可以用二分法在算法上进行优化!
查找表

如果是大量离散值需要进行测试的话,我们可以放弃条件语句,直接变成数组的属性访问。这样子效率更高。

递归

使用递归可以使得复杂的算法变得简单,比如阶乘函数就是递归实现的。

tudo:第四章的有些看不下去了。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 981909093@qq.com

文章标题:DOM编程与算法流程控制

文章字数:2.1k

本文作者:泽鹿

发布时间:2019-08-28, 16:45:23

最后更新:2019-08-28, 16:45:23

原始链接:http://panyifei.github.io/2019/08/28/读书笔记/高性能Javascript/3,4章DOM编程与算法流程控制/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏