性能技巧#

窗口化列表#

窗口化列表算法在由 @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 元素测量无法捕获边距。因此,单元格容器不应使用它。填充是解决方案,因为测量仅限于边框大小。这是因为相邻元素之间的顶部和底部边距 可能会被网页浏览器折叠

视窗状态#

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

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

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

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

笔记本小部件具有以下助手

  • scrollToItem 用于滚动到特定单元格索引。与将元素滚动到视图中一样,可以使用各种模式。