有没有一种方法可以在Web Worker中创建DOM外的元素

Is there a way to create out of DOM elements in Web Worker?

本文关键字:Worker Web 创建 DOM 元素 一种 方法 有没有      更新时间:2023-09-26

上下文:我有一个web应用程序,可以处理和显示巨大的日志文件。它们通常只有大约10万条线路长,但可能高达400万条或更多。为了能够滚动浏览该日志文件(包括用户启动的和通过JavaScript)并以良好的性能过滤行,我在数据到达后立即为每行创建一个DOM元素(通过ajax以JSON形式)。我发现这比在后端构建HTML更能提高性能。之后,我将元素保存在数组中,并且只显示可见的行。

对于最大100k条线路,这只需要几秒钟,但对于500k条线路(不包括下载),任何其他操作都需要一分钟。我想进一步提高性能,所以我尝试使用HTML5WebWorkers。现在的问题是,我不能在WebWorker中创建元素,甚至不能在DOM之外创建元素。因此,我最终只在WebWorkers中进行json到HTML的转换,并将结果发送到主线程。在那里,它被创建并存储在一个数组中。不幸的是,这使性能恶化,现在至少需要30秒的时间。

问题:我不知道有什么方法可以在DOM树之外的Web Worker中创建DOM元素吗?如果没有,为什么不呢?在我看来,这不会产生并发问题,因为创建元素可以并行进行而不会出现问题。

好吧,我对@Bergi提供的信息进行了更多的研究,并在W3C邮件列表上发现了以下讨论:

http://w3-org.9356.n7.nabble.com/Limited-DOM-in-Web-Workers-td44284.html

以下摘录回答了为什么Web Worker中无法访问XML解析器或DOM解析器:

您假设DOM实现代码中没有使用任何排序非DOM对象的数量,或者如果是这样的话,这些对象是完全的线程安全。事实并非如此,至少在壁虎身上是这样。

本例中的问题不在于所接触的DOM对象多线程。问题是不同线程上的两个DOM对象两者都接触某个全局第三对象。

例如,XML解析器必须做一些事情,在Gecko中只能在主线程上完成(DTD加载,随意;有我以前见过但不记得的其他几个)。

然而,也提到了一种变通方法,即使用解析器的第三方实现,jsdom就是一个例子。有了这个,您甚至可以访问自己的单独文档。

那么,我不知道有什么方法可以在DOM树之外的Web Worker中创建DOM元素吗?

没有。

为什么不呢?在我看来,这不会产生并发问题,因为创建元素可以并行进行而不会出现问题。

不是为了创造它们,你是对的。但要将它们附加到主document,则需要将它们发送到不同的内存(就像Blob一样),这样之后工作人员就无法访问它们。但是,WebWorkers中绝对没有可用的文档处理。

数据一到,我就为每一行创建一个DOM元素(通过ajax以JSON形式)。之后,我将元素保存在数组中,并且只显示可见的行。

构建超过50万个DOM元素是一项繁重的任务。尝试只为可见的行创建DOM元素。为了提高性能并更快地显示前几行,您还可以将它们的处理划分为更小的单元,并在其间使用超时。请参阅如何阻止强烈的Javascript循环冻结浏览器

您必须了解网络工作者的性质。使用线程编程是很困难的,尤其是在共享内存的情况下;奇怪的事情可能会发生。JavaScript不具备处理任何类似线程的交织的能力。

网络工作者的方法是没有共享内存。这显然会导致无法访问DOM的结论。

没有直接的方法可以通过Web Workers访问DOM。我最近发布了@cycle/shadbox,它仍然是WIP,但它证明了使用CycleJS架构在WebWorker中声明UI行为是相当直接的。实际的DOM只在主线程中被接触,但事件侦听器和DOM更新是在工作线程中间接声明的,并且当这些侦听器上发生某些事情时,会发送合成的事件对象。此外,将这些沙盒循环组件并排安装到常规循环组件是直接的。

http://github.com/aronallen/-cycle-sandbox/

我看不出有什么理由不能使用web Worker构建html字符串。但我也不认为会有太大的业绩提升。

这与Web Workers无关,但与您试图解决的问题有关。以下是一些可能有助于加快速度的东西:

  1. 使用DocumentFragments。在数据进入时向它们添加元素,并每隔一段时间(比如每秒一次)将片段添加到DOM中。这样,您就不必每次加载一行文本时都触摸DOM(并进行重绘)。

  2. 在后台加载,只在用户点击滚动区域底部时解析行。

您的设计中有几个反模式:

  1. 创建DOM对象有相当大的开销同时潜在地创建数百万个
  2. 试图让一个web工作者来管理DOM正是web工人是而不是。它们执行其他所有操作,以便DOM事件循环保持响应

您可以使用光标模式滚动任意大的数据集。

  1. DOM向worker发布一条消息,其中包含开始位置和请求的行数(游标)
  2. Web工作者随机访问日志,张贴回提取的行(游标数据)
  3. DOM使用异步游标响应事件更新元素

通过这种方式,繁重的工作由工作人员完成,工作人员的事件循环在提取过程中而不是DOM中被阻止,这会让高兴的未被阻止的用户惊叹于你所有的动画是多么流畅。

根据https://developer.mozilla.org/en-US/docs/Web/Guide/Performance/Using_web_workers不幸的是,web工作人员无法访问DOM。

因此,您不能在网络工作者中直接创建DOM,但是,可能还有另一种选择可以在主线程之外进行相当多的处理。

看看我刚刚创建的这个jsPerf:http://jsperf.com/dom-construction-obj-vs-str

从本质上讲,可以发出与从DOM获得的值完全相同的POJSO,并在收到消息后将其转换为DOM对象(毕竟,这是您在返回HTML时所做的;由于不需要进一步的字符串处理,POJSO只是较低的开销)。通过这种方式,您甚至可以执行类似于发出事件侦听器之类的操作(例如,在事件名称前加上"!",并将值映射到某个模板提供的视图参数)。

同时,如果没有可用的DOM解析器,您将需要自己的东西来根据需要转换模板,或者将模板编译为快速的格式。

不,您不能在web工作程序中创建DOM元素,但您可以创建一个函数,接受来自该web工作程序的post消息,即does创建DOM元素。我认为你所寻找的神格叫做掷阵。您需要将其与网络工作者设计模式相结合。

2022年更新(自2018年起实际提供chrome版本):

如果您可以在画布元素中显示日志,则可以使用新的OffsetcreenCanvas api。

"偏移画布"界面提供了一个可以在屏幕外渲染的画布。它在窗口和工作环境中都可用。

然后,您可以将Worker中生成的帧异步显示回主线程上的画布元素。

这里有更多的例子。