性能技巧#

窗口化列表#

列表窗口化算法由 @jupyterlab/ui-components 提供的专用小部件 WindowedList 实现。

此小部件将具有如下 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 节点。但是当打开该选项时,UI 可能会冻结,因为所有从未渲染过的输出都需要渲染。

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

  • HTML 元素测量无法捕获边距。因此,单元格容器不应使用它。内边距是解决方案,因为测量仅限于边框大小。这是因为相邻元素之间的顶部和底部边距 可能会被 Web 浏览器折叠

视口状态#

每个单元格小部件都实现以下三个属性

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

  • ready 返回一个 Promise,当单元格完全实例化时解析。之后,编辑器和任何子项都将存在。

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

笔记本小部件具有以下帮助程序

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