Skip to content

Latest commit

 

History

History
122 lines (77 loc) · 7.31 KB

3,4章DOM编程与算法流程控制.md

File metadata and controls

122 lines (77 loc) · 7.31 KB
layout title
default
{"site.name" => nil}

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:第四章的有些看不下去了。