性能优化:为什么不要频繁操作DOM

@[toc]

性能优化的时候,我们常说“不要频繁操作DOM”,但是“DOM 为什么这么慢”以及“如何使 DOM 变快”呢。

DOM 为什么这么慢,因为,DOM和JS的跨界交流

把 DOM 和 JavaScript 各自想象成一个岛屿,它们之间用收费桥梁连接。——《高性能 JavaScript》

JS 是很快的,在 JS 中修改 DOM 对象也是很快的。在JS的世界里,一切是简单的、迅速的。但 DOM 操作并非 JS 一个人的独舞,而是两个模块之间的协作。

JS 引擎和渲染引擎(浏览器内核)是独立实现的。当我们用 JS 去操作 DOM 时,本质上是 JS 引擎和渲染引擎之间进行了“跨界交流”。

“跨界交流”要收费——这个开销本身就是不可忽略的。我们每操作一次 DOM(不管是为了修改还是仅仅为了访问其值),都要跨界一次。跨界的次数一多,就会产生比较明显的性能问题。因此“减少 DOM 操作”的建议,并非空穴来风。

对 DOM 的修改引发样式的更迭

很多时候,我们对 DOM 的操作都不会局限于访问,而是为了修改它。当我们对 DOM 的修改会引发它外观(样式)上的改变时,就会触发回流或重绘。

这个过程本质上还是因为我们对 DOM 的修改触发了渲染树(Render Tree)的变化所导致的:

回流:当我们对 DOM 的修改引发了 DOM 几何尺寸变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。

重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘。

由此我们可以看出,重绘不一定导致回流,回流一定会导致重绘。硬要比较的话,回流比重绘做的事情更多,带来的开销也更大。但这两个说到底都是吃性能的,所以都不是什么善茬。我们在开发中,要从代码层面出发,尽可能把回流和重绘的次数最小化。

如何使DOM变快

知道了 DOM 慢的原因,我们就可以对症下药了。

减少 DOM 操作:少“跨界交流”

例子,HTML 内容如下:

DOM操作测试

此时我有一个假需求——我想往 container 元素里写 10000 句一样的话。如果我这么做:

for(var count=0;count<10000;count++){

document.getElementById('container').innerHTML+='我是一个小测试'

}

这段代码有两个明显的可优化点。

第一点,跨界交流太多了。我们每一次循环都调用 DOM 接口重新获取了一次 container 元素,相当于每次循环都进行一次“跨界交流”。前后交了 10000 次,但其中 9999 次都可以用缓存变量的方式节省下来:

// 只获取一次container

let container = document.getElementById('container')

for(let count=0;count<10000;count++){

container.innerHTML += '我是一个小测试'

}

第二点,不必要的 DOM 更改太多了。我们的 10000 次循环里,修改了 10000 次 DOM 树。我们前面说过,对 DOM 的修改会引发渲染树的改变、进而去走一个(可能的)回流或重绘的过程,而这个过程的开销是很“贵”的。这么贵的操作,我们竟然重复执行了 N 多次!其实我们可以通过就事论事的方式节省下来不必要的渲染:

let container = document.getElementById('container')

let content = ''

for(let count=0;count<10000;count++){

// 先对内容进行操作

content += '我是一个小测试'

}

// 内容处理好了,最后再触发DOM的更改

container.innerHTML = content

JS 层面的事情,JS 自己去处理,处理好了,再来找 DOM 打报告。

事实上,考虑JS 的运行速度,比 DOM 快得多这个特性。我们减少 DOM 操作的核心思路,就是让 JS 去给 DOM 分压。

这个思路,在 DOM Fragment 中体现得淋漓尽致。

DocumentFragment 接口表示一个没有父级文件的最小文档对象。它被当做一个轻量版的 Document 使用,用于存储已排好版的或尚未打理好格式的XML片段。因为 DocumentFragment 不是真实 DOM 树的一部分,它的变化不会引起 DOM 树的重新渲染的操作(reflow),且不会导致性能等问题。

在我们上面的例子里,字符串变量 content 就扮演着一个 DOM Fragment 的角色。其实无论字符串变量也好,DOM Fragment 也罢,它们本质上都作为脱离了真实 DOM 树的容器出现,用于缓存批量化的 DOM 操作。

前面我们直接用 innerHTML 去拼接目标内容,这样做固然有用,但却不够优雅。相比之下,DOM Fragment 可以帮助我们用更加结构化的方式去达成同样的目的,从而在维持性能的同时,保住我们代码的可拓展和可维护性。我们现在用 DOM Fragment 来改写上面的例子:

let container = document.getElementById('container')

// 创建一个DOM Fragment对象作为容器

let content = document.createDocumentFragment()

for(let count=0;count<10000;count++){

// span此时可以通过DOM API去创建

let oSpan = document.createElement("span")

oSpan.innerHTML = '我是一个小测试'

// 像操作真实DOM一样操作DOM Fragment对象

content.appendChild(oSpan)

}

// 内容处理好了,最后再触发真实DOM的更改

container.appendChild(content)

我们运行这段代码,可以得到与前面两种写法相同的运行结果。

可以看出,DOM Fragment 对象允许我们像操作真实 DOM 一样去调用各种各样的 DOM API,我们的代码质量因此得到了保证。

php 频繁dom和 文件,性能优化之为什么不要频繁操作DOM相关推荐

  1. 浏览器性能优化(2)React 虚拟 dom与diff算法

    随着前端技术快速发展,现在的mvvm几大框架遍布前端行业,那么它们对浏览器的性能到底影响多大?与传统的jq相比做了哪些优化呢? 文章目录: React中的虚拟DOM是什么? 虚拟DOM的简单实现(di ...

  2. redis session 超时时间_Shiro性能优化:解决Session频繁读写问题

    点击上方蓝色字体,选择"标星公众号" 优质文章,第一时间送达 作者 |  张永恒 来源 |  urlify.cn/YjEZNj 背景 Shiro 提供了强大的 Session 管理 ...

  3. java shiro 访问频率_Shiro性能优化:解决Session频繁读写问题

    目录 背景 应对思路 本地缓存 Session 避免不必要的 Session 更新 代码实现 ShiroSessionDAO.java ShiroConfig.java 背景 Shiro 提供了强大的 ...

  4. C++性能优化笔记-11-使用向量操作

    使用向量操作 AVX指令集和YMM寄存器 AVX512指令集和ZMM寄存器 自动向量化 使用内建函数 对齐数据 向量化表查找 使用向量类 向量类的CPU分发 转换串行代码到向量化代码 数学函数的向量化 ...

  5. DOM性能瓶颈与Javascript性能优化

    这两天比较闲,写了两篇关于JS性能缺陷与解决方案的文章(<JS特性性能缺陷及JIT的解决方案>,<Javascript垃圾回收浅析>),主要描述了untyped,GC带来的问题 ...

  6. (转)Javascript的DOM操作 - 性能优化

    转载:https://my.oschina.net/blogshi/blog/198910 摘要: 想稍微系统的说说对于DOM的操作,把Javascript和jQuery常用操作DOM的内容归纳成思维 ...

  7. 从DOM到虚拟DOM——前端DOM发展史、性能与产能双赢背后的思考

    写在前面的一些话 从我上大学开始正式认识前端这个岗位到现在已经整整过了六年,这六年于我而言就像是观赏了一场盛大的游园活动.无数各式各样的框架.模式.开发思路如同花车一般从眼前掠过又走远.留下了一地残乱 ...

  8. 前端性能优化之Lazyload

    前端性能优化之Lazyload @(Mob前端-冬晨)[JavaScript|技术分享|懒加载] [TOC] Lazyload 简介 前端工作中,界面和效果正在变得越来越狂拽炫酷,与此同时性能也是不得 ...

  9. 前端性能优化之Lazyload 1

    一.Lazyload的理解分析 Lazyload技术是一种延迟加载技术,让页面加载速度快到飞起.减轻服务器压力.节约流量.提升用户体验 实现思路 1)页面较长,屏幕的可视区域有限 2)不设置页面中im ...

最新文章

  1. 130242014045 林承晖 第2次实验
  2. uboot中的mmc命令
  3. (转)淘淘商城系列——搜索系统搭建
  4. 神州租车:陆正耀辞任公司董事会主席及非执行董事职务
  5. python获取url的json数据_通过url获取json数据并在python中使用(simplejson)
  6. C语言sin定积分,C语言实现黎曼和求定积分
  7. flutter 接入微信客服
  8. 应用计算机怎么弹ink,Win10不小心按W键结果弹出INK工作区的处理方法
  9. 微信小程序css篇----字体(Font)
  10. 打造黑苹果(四)安装MACOS系统
  11. React hooks - Ref 使用实例
  12. linux极点五笔无法输入词组_ibus设置
  13. 解决闭包问题时 setTimeout执行顺序提前的问题
  14. ajax 源生代码,ajax 源生,jquery封装 例子 相同哈哈
  15. ENC28J60学习笔记——第3部分
  16. hyperf 命令行,自定义命令(更换文件目录不生效 - 需配置)
  17. D/D/O/S学习思维导图
  18. 【C++】万能头文件 <bits/stdc++.h> 的用法和优缺点
  19. Labelmx条码标签打印软件可以做哪些标签?
  20. java撞南墙:桌面应用与web开发

热门文章

  1. linux ntp时间立即同步命令_如何在 Linux 下确认 NTP 是否同步?
  2. c语言我爱你二进制,二憨的爱情c语言表白代码我爱你·爱如流离.DOC
  3. 弹框在一个很的长页面居中显示
  4. Icon class生成器(Python)
  5. 三维重建4:Jacobian矩阵和Hessian矩阵
  6. Matlab atan2
  7. 使用Jmeter 创建Post请求
  8. 算法笔记--字符串hash
  9. CentOS SSH安装与配置
  10. 8.5 趣味游戏(2)