性能技巧#

窗口列表#

列表窗口算法在一个专用的小部件 WindowedList 中实现,该小部件由 @jupyterlab/ui-components 提供。

这个小部件的DOM结构将如下所示:

将可见项封装在这样的树中的原因来自于需要在具有实际高度的伪文档中正确定位当前视图。

笔记本文档#

JupyterLab中笔记本文档的性能分析指出了两个主要瓶颈:编辑器样式计算和包含大量数学表达式的渲染Markdown。缓解这些问题的最佳技术是仅将视口中的单元格附加到DOM。然而,代码单元格输出带来了一个巨大的限制,即它们的内部状态位于DOM节点内(例如地图视图中的缩放级别)。因此,一旦显示,输出就无法分离。考虑到这些因素,以下是针对笔记本窗口化的算法编码。

注意

初始化视图时,会扫描代码单元的输出以检测它们是否包含定义style和/或scripts元素的text/html输出。如果包含,这些单元将被渲染以确保样式和JavaScript被应用,因为它们可能会泄漏到定义单元之外(例如注入自定义样式)。

当单元格小部件被实例化时,它们的子元素(即编辑器、输出等)不会被创建,也不会附加到DOM中。视图在以下滚动事件时更新:

  1. 获取笔记本中的滚动位置

  2. 获取要显示的单元格范围

  3. 将视口中的单元格附加到笔记本 a. 在附加单元格之前,如果它从未被附加过,实例化其子元素。 b. 对于之前附加过的代码单元格,更改其可见性并附加除输出区域外的所有子元素。 c. 将单元格小部件添加到触发视口更新的调整大小观察器中

  4. 分离离开视口的单元格 a. 从调整大小观察器中移除单元格小部件。 b. 对于代码单元格,输出区域不会被分离而是被隐藏。所有其他子元素都会被分离。

由于代码单元格保留在DOM中,当单元格列表发生变化时,我们需要更新数据属性 data-windowed-list-index。这是为了在视口变化时将单元格附加到正确的位置。

列表窗口算法在一个专用的小部件WindowedList中编码。笔记本扩展了它,并使用专门的布局来处理代码单元格,NotebookWindowedLayout。代码单元格布局也进行了自定义CodeCellLayout,以保持输出区域附加,但不附加其他子元素。

警告

当前的实现没有很好地处理iframe。在Chrome中,每次iframe离开并重新进入视口时,其状态都会被重置。在Firefox中,当滚动来自鼠标滚动时,不会发生这种情况。但是,当跳转到视口外的特定位置时,重置会发生;例如,搜索一个单词或导航到一个标题。

实现的副作用#

  • 仍然可以搜索单元格输出的DOM节点。但是当开启该选项时,用户界面可能会冻结,因为所有未渲染的输出都需要被渲染。

  • 单元格编辑器仅适用于至少一次出现在视口中的单元格。 当单元格不在视口中时,编辑器不会被销毁。因此,可以修改其状态。

  • HTML元素的测量无法捕捉到外边距。因此,单元格容器不应使用它。 内边距是解决方案,因为测量仅限于边框大小。这是因为相邻元素之间的上下边距可以被网页浏览器折叠

视口状态#

每个单元格小部件都实现了这三个属性:

  • isPlaceholder 如果单元格从未被附加(因此没有实例化的子元素),则为 false。

  • ready 返回一个 promise,当单元格完全实例化时解析。之后,编辑器及其所有子元素将存在。

  • inViewport 单元格当前是否在视口中。你可以监听信号 inViewportChanged。

笔记本小部件有以下辅助功能:

  • scrollToItem 滚动到特定的单元格索引。就像将元素滚动到视图中一样,有多种模式可用。