没有找到合适的产品?
联系客服协助选型:023-68661681
提供3000多款全球软件/控件产品
针对软件研发的各个阶段提供专业培训与技术咨询
根据客户需求提供定制化的软件开发服务
全球知名设计软件,显著提升设计质量
打造以经营为中心,实现生产过程透明化管理
帮助企业合理产能分配,提高资源利用率
快速打造数字化生产线,实现全流程追溯
生产过程精准追溯,满足企业合规要求
以六西格玛为理论基础,实现产品质量全数字化管理
通过大屏电子看板,实现车间透明化管理
对设备进行全生命周期管理,提高设备综合利用率
实现设备数据的实时采集与监控
利用数字化技术提升油气勘探的效率和成功率
钻井计划优化、实时监控和风险评估
提供业务洞察与决策支持实现数据驱动决策
原创|其它|编辑:郝浩|2009-03-04 17:39:23.000|阅读 458 次
概述:在Web开发中,JavaScript的一个很重要的作用就是对DOM进行操作,可你知道么?对DOM的操作是非常昂贵的,因为这会导致浏览器执行回流操作,而执行了过多的回流操作,你就会发现自己的网站变得越来越慢了,我们应该尽可能的减少DOM操作。本文是这个系列的最后一篇,给出了一些指导性原则,比如在什么时候应该对DOM可以进行什么样的操作等。
# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>
在Web开发中,JavaScript的一个很重要的作用就是对DOM进行操作,可你知道么?对DOM的操作是非常昂贵的,因为这会导致浏览器执行 回流操作,而执行了过多的回流操作,你就会发现自己的网站变得越来越慢了,我们应该尽可能的减少DOM操作。本文是这个系列的最后一篇,给出了一些指导性 原则,比如在什么时候应该对DOM可以进行什么样的操作等。
【原文】Nicholas C. Zakas - Speed up your JavaScript, Part 4
【译文】明达 - 如何提升JavaScript的运行速度(DOM篇)
以下是对原文的翻译:
在过去的几周中,我为大家介绍了几种可以加快JavaScript脚本运行速度的技术。第一节 介绍了如何优化循环。第二节 的重点放在优化函数内部代码上,还介绍了队列(queuing)和记忆化(memoization)两种技术,来减轻函数的工作负担。第三节 就如何将递归转换为迭代循环或者记忆化方式的话题,展开了讨论。第四节是这个系列的最后一篇,也就是本文,将重点阐述过多的DOM操作所带来的影响。
我们都知道,DOM操作的效率是很低的,而且不是一般的慢,而且这也是引发性能问题的常见问题之一。为什么会慢呢?因为对DOM的修改为影响网页的 用户界面,重绘页面是一项昂贵的操作。太多的DOM操作会导致一系列的重绘操作,为了确保执行结果的准确性,所有的修改操作是按顺序同步执行的。我们称这 个过程叫做回流(reflow),同时这也是最昂贵的浏览器操作之一。回流操作主要会发生在几种情况下:
解决问题的关键,就是限制通过DOM操作所引发回流的次数。大部分浏览器都不会在JavaScript的执行过程中更新DOM。相应的,这些浏览器 将对对DOM的操作放进一个队列,并在JavaScript脚本执行完毕以后按顺序一次执行完毕。也就是说,在JavaScript执行的过程中,用户不 能和浏览器进行互动,直到一个回流操作被执行。( 失控脚本对话框 会触发回流操作,因为他执行了一个中止JavaScript执行的操作,此时会对用户界面进行更新)
如果要减少由于DOM修改带来的回流操作,有两个基本的方法。第一个就是在对当前DOM进行操作之前,尽可能多的做一些准备工作。一个经典的例子就是向document对象中添加很多DOM节点:
for (var i=0; i < items.length; i++){
var item = document.createElement("li");
item.appendChild(document.createTextNode("Option " + i);
list.appendChild(item);
}
这段代码的效率是很低的,因为他在每次循环中都会修改当前DOM结构。为了提高性能,我们需要将这个次数降到最低,对于这个案例来说,最好的办法是 建立一个文档碎片(document fragment),作为那些已创建元素元素的临时容器,最后一次将容器的内容直接添加到父节点中:
var fragment = document.createDocumentFragment();
for (var i=0; i < items.length; i++){
var item = document.createElement("li");
item.appendChild(document.createTextNode("Option " + i);
fragment.appendChild(item);
}
list.appendChild(fragment);
经过调整的代码,只会修改一次当前DOM的结构,就在最后一行,而在这之前,我们用文档碎片来保存那些中间结果。因为文档碎片没有任何可见内容,所 以这类修改不会触发回流操作。实际上,文档碎片也不能被添加到DOM中,我们需要将它作为参数传给appendChild函数,而实际上添加的不是文档碎 片本身,而是它下面的所有子元素。
避免不必要回流操作的另外一种方法,就是在对DOM操作之前,把要操作的元素,先从当前DOM结构中删除。对于删除一个元素,基本有两种方法:
而一旦修改操作完成,上面这个过程就需要反转过来,将删除的元素重新添加到当前的DOM结构中,我们还是拿上面的例子来做说明:
list.style.display = "none";
for (var i=0; i < items.length; i++){
var item = document.createElement("li");
item.appendChild(document.createTextNode("Option " + i);
list.appendChild(item);
}
list.style.display = "";
将list的display样式设置为“none”后,就将这个元素从当前的DOM结构中删除了,因为这个节点不再可视。在将display属性设置回之前的默认值之前,向其下添加子元素是不会触发回流操作的。
另外一个经常引起回流操作的情况是通过style属性对元素的外观进行修改。比如下面这个例子:
element.style.backgroundColor = "blue";
element.style.color = "red";
element.style.fontSize = "12em";
这段代码修改了三个样式,同时也就触发了三次回流操作。每次修改元素的style属性,都肯定会触发回流操作。如果你要同时修改一个元素的很多样 式,最好的办法是将这些样式放到一个class下,然后直接修改元素的class,这可比单独修改元素的样式要强得多。比如下面这个例子:
.newStyle {
background-color: blue;
color: red;
font-size: 12em;
}
这样我们在JavaScript代码中,只需下面这行代码就可以修改样式:
/*element.className = "newStyle";*/
修改元素的class属性,会一次将所有的样式应用在目标元素上,而且只会触发一次回流操作。这样做不止更加有效,而且还更容易维护。
既然DOM几乎在所有情况下都很慢,就很有必要将获取的DOM数据缓存起来。这种方法,不仅对获取那些会触发回流操作的属性(比如offsetWidth等)尤为重要,就算对于一般情况,也同样适用。下面介绍一个效率低的夸张的例子:
document.getElementById("myDiv").style.left = document.getElementById("myDiv").offsetLeft +
document.getElementById("myDiv").offsetWidth + "px";
这里对getElementById()调用了三次,是一个很大的问题,访问DOM是很昂贵的,而这三个调用恰恰访问的是同一个元素,也许我们像下面这样写,会更好一些:
var myDiv = document.getElementById("myDiv");
myDiv.style.left = myDiv.offsetLeft + myDiv.offsetWidth + "px";
我们去掉了一些冗余操作,现在对DOM操作的次数已经被减小了。对于那些使用次数超过一次的DOM值,我们都应该缓冲起来,这样可以避免无谓的性能消耗。
也许,拖慢属性访问速度的罪魁祸首就是HTMLCollection对象。这些对象是object类型的,只要DOM需要返回一组节点时就会使用这 个对象,也就是说childNodes属性和getElementsByTagName()的返回值都属于这种情况。我们可能经常会将 HTMLCollection当作数组来使用,但实际上他是一个根据DOM结构自动变化的实体对象。每次你访问一个HTMLCollection对象的属 性,他都会对DOM内所有的节点进行一次完整匹配,这意味着下面的代码将导致一个死循环:
var divs = document.getElementsByTagName("div");
for (var i=0; i < divs.length; i++){ //infinite loop
document.body.appendChild(document.createElement("div"));
}
这段代码为什么会变成死循环呢?因为在每次循环中,将会向document中新增一个div元素,同时也会更新divs这个集合,也就是说循环的索 引永远都不会超过divs.length的值,因为divs.length的值是伴随着循环而递增的。每次访问divs.length,就会更新一次集合 对象,这可比访问一个普通数组的length属性要付出更大的代价。当对HTMLCollection对象进行操作时,应该将访问的次数尽可能的降至最 低,最简单的,你可以将length属性缓存在一个本地变量中,这样就能大幅度的提高循环的效率。
var divs = document.getElementsByTagName("div");
for (var i=0, len=divs.length; i < len; i++){ //not an infinite loop
document.body.appendChild(document.createElement("div"));
}
修改后的代码已经不是死循环了,因为在每次循环时,len的值都是保持固定不变的。将属性值缓存起来除了更加有效率,还可以保证document不会执行多于一次的查询。
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com
文章转载自:蓝色理想面对“数字中国”建设和中国制造2025战略实施的机遇期,中车信息公司紧跟时代的步伐,以“集约化、专业化、标准化、精益化、一体化、平台化”为工作目标,大力推进信息服务、工业软件等核心产品及业务的发展。在慧都3D解决方案的实施下,清软英泰建成了多模型来源的综合轻量化显示平台、实现文件不失真的百倍压缩比、针对模型中的大模型文件,在展示平台上进行流畅展示,提升工作效率,优化了使用体验。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
服务电话
重庆/ 023-68661681
华东/ 13452821722
华南/ 18100878085
华北/ 17347785263
客户支持
技术支持咨询服务
服务热线:400-700-1020
邮箱:sales@evget.com
关注我们
地址 : 重庆市九龙坡区火炬大道69号6幢
慧都科技 版权所有 Copyright 2003-
2025 渝ICP备12000582号-13 渝公网安备
50010702500608号