如何提升JavaScript的运行速度(DOM篇)[译]

作者:明达 来源:七月佑安 时间:2009-02-25 12:24:00 

在Web开发中,JavaScript的一个很重要的作用就是对DOM进行操作,可你知道么?对DOM的操作是非常昂贵的,因为这会导致浏览器执行回流操作,而执行了过多的回流操作,你就会发现自己的网站变得越来越慢了,我们应该尽可能的减少DOM操作。本文是这个系列的最后一篇,给出了一些指导性原则,比如在什么时候应该对DOM可以进行什么样的操作等。

【原文】Nicholas C. Zakas - Speed up your JavaScript, Part 4

以下是对原文的翻译:

在过去的几周中,我为大家介绍了几种可以加快JavaScript脚本运行速度的技术。第一节介绍了如何优化循环。第二节的重点放在优化函数内部代码上,还介绍了队列(queuing)和记忆化(memoization)两种技术,来减轻函数的工作负担。第三节就如何将递归转换为迭代循环或者记忆化方式的话题,展开了讨论。第四节是这个系列的最后一篇,也就是本文,将重点阐述过多的DOM操作所带来的影响。

我们都知道,DOM操作的效率是很低的,而且不是一般的慢,而且这也是引发性能问题的常见问题之一。为什么会慢呢?因为对DOM的修改为影响网页的用户界面,重绘页面是一项昂贵的操作。太多的DOM操作会导致一系列的重绘操作,为了确保执行结果的准确性,所有的修改操作是按顺序同步执行的。我们称这个过程叫做回流(reflow),同时这也是最昂贵的浏览器操作之一。回流操作主要会发生在几种情况下:

* 当对DOM节点执行新增或者删除操作时。
* 动态设置一个样式时(比如element.style.width="10px")。
* 当获取一个必须经过计算的尺寸值时,比如访问offsetWidth、clientHeight或者其他需要经过计算的CSS值(在兼容DOM的浏览器中,可以通过getComputedStyle函数获取;在IE中,可以通过currentStyle属性获取)。

解决问题的关键,就是限制通过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函数,而实际上添加的不是文档碎片本身,而是它下面的所有子元素。

标签:JavaScript,速度,dom,速度
0
投稿

猜你喜欢

  • ChatGPT帮我看下这段代码有什么问题

    2022-09-02 15:37:20
  • 太有才了!让人称绝的404错误页面

    2007-08-19 15:51:00
  • Keras使用ImageNet上预训练的模型方式

    2021-03-01 10:08:51
  • Python中的异常处理相关语句基础学习笔记

    2021-10-18 00:54:50
  • python3连接mysql获取ansible动态inventory脚本

    2024-01-19 23:13:54
  • 如何用python爬取微博热搜数据并保存

    2021-10-21 14:13:38
  • linux下perl操作mysql数据库(需要安装DBI)

    2024-01-15 09:32:50
  • Python如何调用JS文件中的函数

    2022-11-21 01:23:11
  • python实现Virginia无密钥解密

    2023-07-25 10:56:49
  • Minio设置文件链接永久有效的完整步骤

    2023-06-10 22:26:10
  • js 获取、清空input type="file"的值示例代码

    2024-04-22 13:07:14
  • Python中的sys.stdout.write实现打印刷新功能

    2022-01-17 14:51:50
  • mybatis如何实现的数据库排序

    2024-01-24 16:35:32
  • GitLab使用外部提供的Redis缓存数据库的方法详解

    2024-01-23 10:02:57
  • python 筛选数据集中列中value长度大于20的数据集方法

    2023-05-27 03:55:02
  • Vue实现未登录跳转到登录页的示例代码

    2023-07-02 17:02:49
  • 基于Python3制作一个带GUI界面的小说爬虫工具

    2023-04-02 17:33:04
  • python中扫描条形码和二维码的实现代码

    2023-02-15 23:00:12
  • MySQL学习第四天 Windows 64位系统下使用MySQL

    2024-01-24 02:35:46
  • python合并多个excel的详细过程

    2023-10-03 14:39:26
  • asp之家 网络编程 m.aspxhome.com