sim*_*mon 13 javascript performance dom
我有一个HTML/JS网站,里面有几百个div元素.应该一次快速更新几十个这些元素(每秒最多250次)(即它们应该一次更新,而不需要浏览器通过逐个执行更新来执行不必要的工作一).什么是最高性能的方法(最小化CPU负载和内存消耗)来支持现代浏览器使用纯JavaScript或简单库实现这一点(不使用React或类似的库,要求我修改DOM处理之外的代码) )?我正在寻找类似这样的东西(imaginaryLibrary是我不知道的库,我正在寻找):
var i, element;
for(i = 0; i < 20; i++){
element = document.getElementById('el' + i);
imaginaryLibrary.collectDomUpdate(element, cssClass, 'updatedCssClass');
}
//executes DOM updates collected in loop before and applies the DOM updates in one reflow
imaginaryLibrary.applyUpdates();
Run Code Online (Sandbox Code Playgroud)
要更新的元素的父元素包含数千个不应更新的元素.
jfr*_*d00 27
你并不是真正具体地了解你正在做什么,所以我们在这里做的最好的事情是给你一些相关领域的一般建议.
DOM修改
您没有完全填写您要尝试的所有细节,但在优化代码的工作方式时,需要考虑以下事项:
回流已经排队. 浏览器已经尝试最大限度地减少回流,因此如果您在一个连续的Javascript中进行四次dom修改,浏览器将等待该Javascript完成运行,然后进行一次重排和一次重绘.
请求某些属性可以触发立即回流.您希望确保避免上述规则的例外情况.例如,如果您请求某些DOM属性需要正确布局以准确报告属性值,并且先前的修改中存在待处理的布局,则浏览器可以在返回您请求的属性之前同步地重新布局文档.这些类型的属性通常涉及屏幕位置和其他属性,这些属性显然会受到文档布局的影响.如果您想了解更多详细信息,有很多关于此主题的文章.在许多情况下,您的代码无论如何都不会使用这些属性,但如果是这样,通常的解决方法是在对DOM进行任何更改之前首先请求所有需要的属性.
一次批量处理所有DOM更改. 最糟糕的事情是进行DOM更改,使用计时器等待几毫秒,进行另一个DOM更改,使用计时器等待几毫秒,等等,因为您将进行DOM更改,重排,重绘,更改DOM,重排,重新绘制等...相反,请确保您在一个同步的Javascript中同时执行所有挂起的DOM更改.然后,这将允许浏览器对重排进行排队并重新绘制,并在完成所有DOM更改后再执行一次.如果您想更好地了解批处理,您将折叠对同一元素的修改,以便仅使用最终值处理一次.因此,如果elementA首先被赋予新值3,然后在同一批次中被赋予值4,则在处理数据批时,您希望跳过3并且只处理4.
有时可以优化DOM修改. 您没有向我们提供有关如何修改DOM的任何细节,但如果您要修改复杂对象(例如添加许多表行或更改大量表格单元格),那么创建一组DOM元素有时会更好当前没有插入DOM,对它们进行所有修改,然后通过一个操作将它们插入到DOM中.这是因为修改当前插入DOM中的DOM元素会强制浏览器确定哪些其他DOM元素受此更改影响,因此它可以排队相应的重排或重新绘制.但是,在将一个较大的对象插入DOM的最后一步之前,修改屏幕外DOM元素不必进行任何工作.这里没有有用的通用规则,因为您如何利用此优化完全取决于您正在进行的DOM修改.我们可以帮助您解决这个问题,但只有当我们能够看到您拥有的HTML以及您正在对其进行的更改时.
更新时间
现在,至于时间的关系,你提到每秒最多250次.对于用户可见的东西,这种速度很快(每次操作4ms).如果你这样做,你的浏览器基本上将不断回流和重新绘制,只是偶尔暂停来处理其他用户事件.由于没有用户能够在4ms,8ms和12ms之后实际看到发生的事情,所以通常不需要在屏幕上更新状态.
因此,如果您确实有快速或经常发生的更改,您可能希望通过将它们累积到本地数据结构中来批处理它们,然后每100-500ms左右更新一次屏幕.你必须试验你可以使用的长间隔,而不是真正注意到任何延迟.
批处理更新有几种实现策略.我能想到的最简单的方法是,如果你的修改总是在不断地进行流式传输,那么只需将修改放入本地数组中,然后只需为你的更新间隔设置一个间隔计时器来检查数组以及是否有任何内容. ,它将数组中的所有项目处理为DOM更新.
从服务器获取更改
您没有提到浏览器Javascript如何获取新数据.通常有两个选项,重复Ajax请求或创建webSocket,服务器可以随时直接向您发送数据.
如果您的更新定期且间隔很短,那么webSocket连接显然是最有效的.它是一个持续连接,只要服务器有新数据要发送,服务器就可以向每个客户端发送新数据.
如果您要使用Ajax进行轮询,那么我强烈建议您延长轮询间隔.非常短的轮询间隔将真正加载您的服务器并在客户端上吃电池.
电池
如果这个应用程序打算在电池供电的设备上长时间运行,那么从任何服务器实时获取稳定的数据流(例如,如你所提到的每隔几毫秒)就会因为收音机而无法使用电池(WiFi)几乎所有时间都会处于活动状态,并且CPU也会运行很多.
小智 6
这似乎与您正在寻找的虚构库类似,只不过它在每次 rAF 之后自动应用更改,而不是开发人员手动处理它:
https://github.com/wilsonpage/fastdom
FastDom 充当应用程序/库和 DOM 之间的监管层。通过批处理 DOM 访问,我们可以避免不必要的文档重排并显着提高布局性能。
每个测量/变异作业都会添加到相应的测量/变异队列中。使用 window.requestAnimationFrame 在下一帧时清空队列(读取,然后写入)。
库大小为 0.6kb gzipped/minified。
看起来您已经具备了想做的事情的基础知识。对于您的特定用例,我认为您没有理由不应该天真地实现这些方法。
var imaginaryLibrary = function() {
var toProcess = [];
function collectDomUpdate(el, prop, newValue) {
toProcess.push([el, prop, newValue])
}
function applyUpdates() {
while (toProcess.length) {
var actions = toProcess.pop()
actions[0][actions[1]] = actions[2]
}
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码是您可能想要做的事情的骨架 - 它不包含错误检查并直接分配给对象而不是潜在地使用 DOM 属性。
| 归档时间: |
|
| 查看次数: |
6265 次 |
| 最近记录: |