博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
读高性能JavaScript编程 第三章
阅读量:5757 次
发布时间:2019-06-18

本文共 5156 字,大约阅读时间需要 17 分钟。

第三章  DOM Scripting 

  1. 最小化 DOM 访问,在 JavaScript 端做尽可能多的事情。 
  2.   在反复访问的地方使用局部变量存放 DOM 引用. 
  3. 小心地处理 HTML 集合,因为他们表现出“存在性”,总是对底层文档重新查询。将集合的 length 属性缓
  4. 存到一个变量中,在迭代中使用这个变量。如果经常操作这个集合,可以将集合拷贝到数组中。
  5.    如果可能的话,使用速度更快的 API,诸如 querySelectorAll()和 firstElementChild。 
  6.    注意重绘和重排版;批量修改风格,离线操作 DOM 树,缓存并减少对布局信息的访问。 
  7.   动画中使用绝对坐标,使用拖放代理。 
  8.    使用事件托管技术最小化事件句柄数量。 
  9. 上面八条建议都是抄的。

dom 文档对象模型 类似的还有 bom 浏览器对象模型 。

要使用一个 对象首先要知道该对象存在的位置,比如 document 对象 它并不是被定义在 ECMAScript 中而是 dom 中。所以 本来你每次访问 document就很慢。

引用:

  文档对象模型(DOM)是一个独立于语言的,使用 XML和 HTML 文档操作的应用程序接口(API)。

在浏览器中,主要与 HTML 文档打交道,在网页应用中检索 XML 文档也很常见。DOM APIs 主要用于访
问这些文档中的数据。

  尽管 DOM 是与语言无关的 API,在浏览器中的接口却是以 JavaScript 实现的。客户端大多数脚本程序

与文档打交道,DOM 就成为 JavaScript 代码日常行为中重要的组成部分。

  浏览器通常要求 DOM 实现和 JavaScript 实现保持相互独立。 例如, 在 Internet Explorer中, 被称为 JScript

的 JavaScript实现位于库文件 jscript.dll中,而 DOM 实现位于另一个库 mshtml.dll(内部代号 Trident)。

两个独立的部分以功能接口连接就会带来性能损耗,这就是为什么会有上面8条建议。

注意:

  1、html集合的存在性

eg:document.getElementsByName()  返回一个类数组对象,这个对象就是 HTML集合。

eg:

// an accidentally infinite loop var alldivs = document.getElementsByTagName_r('div'); for (var i = 0; i < alldivs.length; i++) {   document.body.appendChild(document.createElement('div')) }

    说明:这段代码看上去只是简单地倍增了页面中 div元素的数量。它遍历现有 div,每次创建一个新的 div并附

加到 body上面。但实际上这是个死循环,因为循环终止条件 alldivs.length 在每次迭代中都会增加,它反

映出底层文档的当前状态。

其实这里还有第二个问题,如果循环里没有做任何操作,这是一个正常的循环,但它还是存在性能问题,因为目标是一个HTML集合。

简单说就是每次循环 都会访问 length 属性,而length属性的值是通过查询文档的操作得到的。每获取一次length都会重新查询一次文档,以保证获取到的数据是最新的,上面有一个现成的例证。更要命的是查询文档这个操作是天生就慢的。原因就在上面,有一段话可以加深印象:

  一个很形象的比喻是把 DOM 看成一个岛屿,把 JavaScript(ECMAScript)看成另一个岛屿,两者之间以一座收费桥连接(参见 John Hrvatin,微软,MIX09,http://videos.visitmix.com/MIX09/T53F)。每次 ECMAScript 需要访问 DOM 时,你需要过桥,交一次“过桥费”。

所以,在循环的时候 把 length缓存到一个变量里最好。

 

如果把 alldivs 复制到一个数组内 例如 :

function toArray(coll) {   for (var i = 0, a = [], len = coll.length; i < len; i++) {     a[i] = coll[i];   }   return a; }

可以更优的解决问题,如果你需要在循环中多次操作item的话(建议4)。 同时 len = coll.length 这样写是非常值得学习的,但是在C#中就不晓得有没有益了。

  2、使用速度更快的api,这是一种更优的替代解决方案 eg:  var elements = document.getElementById('menu').getElementsByTagName_r('a');  elements =

toArray(elements); 和 var elements = document.querySelectorAll('#menu a');  建议5:这两个函数都是 DOM 节点的属性,所以你可以使用 document.querySelector('.myclass')来查询整个文档中的节点,或者使用 elref.querySelector('.myclass')在子树中进行查询,其中 elref是一个 DOM 元素的引用。 3、 重绘和重拍版引用原文的解释:

  当浏览器下载完所有页面 HTML标记,JavaScript,CSS,图片之后,它解析文件并创建两个内部数据

结构:    A DOM tree       表示页面结构   A render tree  表示 DOM 节点如何显示 。

  渲染树中为每个需要显示的 DOM 树节点存放至少一个节点(隐藏 DOM 元素在渲染树中没有对应节

点)。渲染树上的节点称为“框”或者“盒”,符合 CSS 模型的定义,将页面元素看作一个具有填充、边距、
边框和位置的盒。一旦 DOM 树和渲染树构造完毕,浏览器就可以显示(绘制)页面上的元素了。

  当 DOM 改变影响到元素的几何属性(宽和高)——例如改变了边框宽度或在段落中添加文字,将发生

一系列后续动作——浏览器需要重新计算元素的几何属性,而且其他元素的几何属性和位置也会因此改变
受到影响。浏览器使渲染树上受到影响的部分失效,然后重构渲染树。这个过程被称作重排版。重排版完
成时,浏览器在一个重绘进程中重新绘制屏幕上受影响的部分。

  不是所有的 DOM 改变都会影响几何属性。例如,改变一个元素的背景颜色不会影响它的宽度或高度。

在这种情况下,只需要重绘(不需要重排版),因为元素的布局没有改变。

 

例子:(有可能根本不会出现,只为分析问题使用)
// setting and retrieving styles in succession var computed,     tmp = '',     bodystyle = document.body.style; if (document.body.currentStyle) { // IE, Opera   computed = document.body.currentStyle; } else { // W3C   computed = document.defaultView.getComputedStyle(docume} // inefficient way of modifying the same property // and retrieving style information right after bodystyle.color = 'red'; tmp = computed.backgroundColor; bodystyle.color = 'white'; tmp = computed.backgroundImage; bodystyle.color = 'green'; tmp = computed.backgroundAttachment; PK: bodystyle.color = 'red'; bodystyle.color = 'white'; bodystyle.color = 'green'; tmp = computed.backgroundColor; tmp = computed.backgroundImage; tmp = computed.backgroundAttachment; //winner
bodystyle.color 虽然和 computed.backgroundColor等三个属性无关,但是 浏览器仍要刷新渲染队列并重排版,注意computed被查询了。(如果把computed缓存到变量里就不会有这种情况) 这也是为什么 后者PK胜出了。 还有一种情况更加具有画面感,
var el = document.getElementById('mydiv'); el.style.borderLeft = '1px'; el.style.borderRight = '2px'; el.style.padding = '5px';

这段代码使浏览器排版了三次。只不过速度比较快感官上只改变了一次,但是可以想象到绘制三次的画面。

(注意:现在大多数浏览器都优化了这种情况,只排版一次。) 

如同下面代码一样:

var el = document.getElementById('mydiv'); el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';  el.style.cssText += '; border-left: 1px;';  //orel.className = 'active'; //or

  离线dom 很好理解,就是在 操作dom的时候不产生 重绘、重排,操作完成后一次性 ‘提交’。

离线dom的三种方式: 1、隐藏 and 操作 and 显示 eg:

var ul = document.getElementById('mylist'); ul.style.display = 'none'; appendDataToElement(ul, data); ul.style.display = 'block';

2、创建并更新一个文档片断,然后附加或替换,eg:

var fragment = document.createDocumentFragment(); appendDataToElement(fragment, data); document.getElementById('mylist').appendChild(fragment);

3、制作副本,替换原节点eg:

var old = document.getElementById('mylist'); var clone = old.cloneNode(true); appendDataToElement(clone, data); old.parentNode.replaceChild(clone, old);

缓存并减少对布局信息的访问就是类似 于提取局部变量,只不过布局信息这类数据的访问影响比较大。

4、动画

  1. 使用绝对坐标定位页面动画的元素,使它位于页面布局流之外。
  2. 比如一个扩大和缩小的动画,当扩大使其覆盖原有布局,重绘这一部分而不会重排、重绘一大部分页面。
  3. 动画结束时,重新定位。 其他元素是一起被复位的。

5、事件托管技术最小化事件句柄数量

这里说自己的理解有可能是错误的,暂时先这样理解了:

首先 每个事件都有三个阶段: 

• Capturing

捕获
• At target
到达目标
• Bubbling
冒泡

在onload(或DOMContentReady) 事件里  会让 很多元素和很多事件句柄 挂接/附加 (attached) ,而 有一些挂接是不必的。比如说一些根本不会被点到的按钮。

引用:

一个简单而优雅的处理 DOM 事件的技术是事件托管。它基于这样一个事实:事件逐层冒泡总能被父元

素捕获。采用事件托管技术之后,你只需要在一个包装元素上挂接一个句柄,用于处理子元素发生的所有
事件。

 

转载于:https://www.cnblogs.com/zhuwansu/p/6138542.html

你可能感兴趣的文章
自定义编译安装python简单笔记
查看>>
企业CIO如何选择好合适的云计算ERP
查看>>
PHP异步执行
查看>>
Python 标准库 18.1 - socket
查看>>
服务器上面文件不能复制到本地方法
查看>>
一个通过struts2获取多选框(checkbox)的坑
查看>>
纯jfinal实现,数据库表自动创建实体类
查看>>
mysql 笔记
查看>>
Supervisor&Gunicorn&Django
查看>>
1.8 uniq和tee命令
查看>>
修改keystore条目的密码
查看>>
Spring Boot 学习笔记2 - Spring Bean 和依赖
查看>>
分享到微信微博空间等第三方平台的JS代码
查看>>
python装饰器合并
查看>>
fiddler2抓包工具使用图文教程
查看>>
作为开发者,你不应该害怕的8件事
查看>>
Cookie跨域操作
查看>>
Mybaties批量更新
查看>>
ubuntu14.04 安装HAXM(KVM)提升android虚拟机Android x8运行速度
查看>>
SpringMVC4 + Hibernate4 整合, 使用Java配置
查看>>