文档帮助

术语、图标和标签

许多类在使用配置对象创建(实例化)类时都有快捷名称。快捷名称称为 alias(或如果类扩展了 Ext.Component,则称为 xtype)。别名/xtype 列在适用类的类名旁边,以便快速参考。

访问级别

框架类或其成员可以指定为 privateprotected。否则,类/成员为 publicPublicprotectedprivate 是访问描述符,用于传达应该如何以及何时使用类或类成员。

成员类型

成员语法

下面是一个示例类成员,我们可以对其进行剖析,以显示类成员的语法(在本例中是从 Ext.button.Button 类查看的 lookupComponent 方法)。

lookupComponent ( item ) : Ext.Component
protected

当原始配置对象添加到此容器时调用,无论是在 items 配置的初始化期间,还是在 添加 或 {@link #insert 插入} 新项目时。

此方法将传递的对象转换为实例化的子组件。

当需要对子项创建应用特殊处理时,可以在子类中覆盖此方法。

参数

item :  Object

正在添加的配置对象。

返回值
Ext.Component

要添加的组件。

让我们看一下成员行的每个部分

成员标志

API 文档使用许多标志来进一步传达类成员的功能和意图。标签可以用文本标签、缩写或图标表示。

类图标

- 表示框架类

- Singleton 框架类。 *有关更多信息,请参阅 singleton 标志

- 组件类型框架类(Ext JS 框架中扩展 Ext.Component 的任何类)

- 表示类、成员或指南在当前查看的版本中是新的

成员图标

- 表示类型为 config 的类成员

- 表示类型为 property 的类成员

- 表示类型为 method 的类成员

- 表示类型为 event 的类成员

- 表示类型为 theme variable 的类成员

- 表示类型为 theme mixin 的类成员

- 表示类、成员或指南在当前查看的版本中是新的

类成员快速导航菜单

在 API 文档页面上的类名正下方是一行按钮,对应于当前类拥有的成员类型。每个按钮都显示按类型划分的成员计数(此计数在应用过滤器时更新)。单击按钮将导航到该成员部分。将鼠标悬停在成员类型按钮上将显示该类型的所有成员的弹出菜单,以便快速导航。

Getter 和 Setter 方法

与类配置选项相关的 Getter 和 Setter 方法将在方法部分以及 API 文档和成员类型菜单的配置部分中显示,就在它们使用的配置下方。Getter 和 Setter 方法文档将在配置行中找到,以便于参考。

历史记录栏

您的页面历史记录保存在本地存储中,并显示在顶部标题栏的正下方(使用可用的实际空间)。默认情况下,显示的唯一搜索结果是与您当前查看的产品/版本匹配的页面。您可以通过单击历史记录栏右侧的 按钮并选择“全部”单选按钮来扩展显示的内容。这将显示所有产品/版本历史记录栏中的所有最近页面。

在历史记录配置菜单中,您还将看到最近访问页面的列表。结果按“当前产品/版本”和“全部”单选按钮过滤。单击 按钮将清除历史记录栏以及保存在本地存储中的历史记录。

如果在历史记录配置菜单中选择“全部”,则将启用“在历史记录栏中显示产品详细信息”的复选框选项。选中后,每个历史页面的产品/版本将在历史记录栏中与页面名称一起显示。将光标悬停在历史记录栏中的页面名称上也会将产品/版本显示为工具提示。

搜索和过滤器

可以使用页面顶部的搜索字段搜索 API 文档和指南。

在 API 文档页面上,还有一个过滤器输入字段,用于使用过滤器字符串过滤成员行。除了按字符串过滤外,您还可以按访问级别、继承和只读过滤类成员。这是通过使用页面顶部的复选框完成的。

API 类导航树底部的复选框过滤类列表,以包含或排除私有类。

单击空的搜索字段将显示您最近 10 次搜索,以便快速导航。

API 文档类元数据

每个 API 文档页面(Javascript 原始类型页面除外)都有一个菜单视图,其中包含与该类相关的元数据。此元数据视图将具有以下一项或多项

展开和折叠示例和类成员

可运行的示例 (Fiddles) 默认在页面上展开。您可以使用代码块左上角的箭头单独折叠和展开示例代码块。您还可以使用页面右上角的切换按钮切换所有示例的折叠状态。所有切换状态将在页面加载之间记住。

类成员默认在页面上折叠。您可以使用成员行左侧的箭头图标或全局使用右上角的展开/折叠所有切换按钮来展开和折叠成员。

桌面 -vs- 移动视图

在较窄的屏幕或浏览器上查看文档将导致针对较小外形尺寸优化的视图。桌面视图和“移动”视图之间的主要区别在于

查看类源代码

可以通过单击 API 文档页面顶部的类名来查看类源代码。可以通过单击成员行右侧的“view source”链接来查看类成员的源代码。

Ext JS 7.8.0


顶部

识别内存泄漏

“内存泄漏”一词在许多上下文中使用。它通常用于描述内存增长。维基百科将“内存泄漏”定义为

“当计算机程序错误地管理内存分配时”。

这是一个合理的定义,但有点模糊。

我们的定义

就本指南而言,内存泄漏定义为

在重复一段代码后,内存使用量无限制增长时。代码必须重复“到耗尽”(足以需要回收内存),并且代码还必须确保已执行合理的语言/框架清理。

这有点拗口,所以让我们分解这个定义的重要部分

语言/框架清理

根据程序运行的环境,通常有一些关于您应该采取的操作的规则,以表明您已完成对某个已分配内存块的使用。在 Ext JS 中,这通常是 destroy 方法,它通常会清理 DOM 元素并取消绑定监听器。

在 C# 中,推荐的模式是 IDisposable 接口。无论平台如何,都必须遵循这些约定,以允许平台释放已分配的资源。如果未遵循清理程序,则会导致内存泄漏,因为无法自动推断何时不再需要资源。

重复到耗尽

假设有一台开发机器,具有 64Gb 的可用内存。一段代码运行 5 次。通过检查,注意到每次运行后,内存使用量每次增加 1Mb,并且永远不会回收。

这种观察结果实际上并不表示有问题。该程序仅使用可用内存的一小部分。如果代码段重复 50,000 次,并且仍然没有回收任何内存,那将是不同的结果。需要充分强调底层系统,使其被迫回收内存。

使用量无限增长

这可能是定义中最微妙但最重要的部分。在许多情况下,调用 destroy 或其他清理可能不会释放所有已分配的资源。在 Ext JS 中,这通常在其缓存中观察到。

例如,Ext.ComponentQuery 类用于根据字符串选择器搜索组件。在内部,此字符串选择器被转换为可以在候选组件上执行的函数。构造此函数成本很高,并且通常,相同的查询会多次运行。由于这种重用,生成的函数保留在内存中。这里的关键点是缓存机制是有界的。

缓存是 LRU(最近最少使用)缓存。LRU 跟踪对集合中项目的访问。当访问项目时,它会被拉到前面。LRU 缓存也有最大大小。当添加项目超过最大大小时,最近最少使用的项目将从缓存中逐出。一旦达到最大限制,缓存就会规范化。这种性质的东西保留在内存中没有问题。只有当资源无限期地保留时,才会成为问题。

抽象和垃圾回收

使用 Ext JS 的开发人员远离真正的内存管理。更糟糕的是,诸如 Window 任务管理器或 Mac 活动监视器之类的工具无法准确描述内存消耗。为了更好地理解因果关系有多远,评估内存管理层非常重要。

分配

  • 开发人员从框架请求资源(例如,创建组件)。
  • 框架从 JavaScript 引擎请求资源(通常使用运算符 new 或 createElement 等)。
  • JavaScript 引擎从底层进程内存管理器请求资源(通常是 C++ 内存分配)。
  • 底层内存管理器从操作系统请求资源。这是我们可以在任务管理器和活动监视器中观察到的内存增长。

清理

  1. 开发人员在 Ext JS 组件或其他资源上调用 destroy。
  2. Ext JS 组件的 destroy 方法调用其他清理方法,将各种内部引用设置为 null 等。
  3. JavaScript 垃圾回收器稍后决定何时扫描堆并回收内存。这通常会延迟到请求新内存并且“没有足够”可用内存时。内存管理器可能只是决定再次增加堆而不是收集垃圾,因为增加堆通常更便宜,尤其是在应用程序生命周期的早期。
  4. 一旦 JavaScript 内存管理器决定收集垃圾,它必须决定是否应将回收的内存保留为可用内存以供将来使用,还是返回到底层进程堆。
  5. 根据 JavaScript 内存管理器(通常是 C++ 内存管理器)使用的底层内存管理器,可用内存可能会保留供该进程将来使用,或者返回给操作系统。只有当我们达到这一点时,我们才能在任务管理器/活动监视器中看到任何更新。

鉴于以上所述,很明显,JavaScript 开发人员对内存管理方面的大局几乎没有控制权。有很多活动部件,真正的内存管理只是一个小齿轮。

就本指南而言,我们将不再进一步讨论这些层。可以说,JavaScript 堆及其垃圾回收器执行他们认为合适的动作,并且不可能强迫它们以特定的方式运行。我们能做的最好的事情是确保引用未被用户代码或框架持有。

最终,使用常见的 OS 监视工具检查内存使用情况并观察到增长并不一定表示“内存泄漏”。

检测泄漏

应用程序级别泄漏

当应用程序未能清理框架资源时,这可能会导致对象在框架维护的多个集合中累积。虽然这些集合的确切细节是特定于版本的,但一些检查的地方是

框架级别泄漏

虽然已尽一切努力清理框架内部的资源,但总有出错的余地。从历史上看,最常见的问题来自泄漏的 DOM 元素。

注意: 我们强烈建议您在查看较低级别的内容之前解决所有应用程序级别的泄漏。

常见代码泄漏模式和解决方案

以下代码片段和描述将突出显示可能导致问题的各种内存滥用方式。

阻止基类清理

为了清理派生类中的资源,可能会意外绕过基类清理。

例如

Ext.define('Foo.bar.CustomButton', {
    extend: 'Ext.button.Button',
    onDestroy: function () {
        // do some cleanup
    }
});

解决方案: 务必调用 callParent(),这允许基类执行其清理。

不删除 DOM 监听器

事件附加到元素。元素被更改 innerHTML 覆盖。但是,此事件处理程序将保留在内存中。

Ext.fly(someElement).on('click', doSomething);

someElement.parentNode.innerHTML = '';

解决方案: 保留对重要元素的引用,并在不再需要它们时调用其 destroy 方法。

保留对对象的引用

创建了使用大量内存的类的实例。该类被销毁,但引用保留在现有对象上。

Ext.define('MyClass', {

    constructor: function() {
        this.foo = new SomeLargeObject();
    },

    destroy: function() {
        this.foo.destroy();
    }
});

this.o = new MyClass();
o.destroy();

// `this` still has a reference to `o` and `o` has a reference to `foo`.

解决方案: 将引用设置为 null 以确保可以回收内存。在这种情况下,在 destroy 中使用 this.foo = null,并在调用 destroy 后使用 this.o = null

在闭包中保留引用

这种情况更微妙,但与上述情况非常相似。闭包持有对大型对象的引用,该对象在仍然引用闭包时无法回收。

function runAsync(val) {
    var o = new SomeLargeObject();
    var x = 42;

    // other things

    return function() {
        return x;  // o is in closure scope but not needed
    }
}

var f = runAsync(1);

上述情况经常发生,因为大型对象存在于外部作用域中,并且内部函数不需要它。这些事情很容易被忽略,但会对内存使用产生负面影响。

解决方案: 使用 Ext.Function.bind() 或标准 JavaScript Function bind 为在此类函数外部声明的函数创建安全闭包。

function fn (x) {
    return x;
}

function runAsync(val) {
    var o = new SomeLargeObject();
    var x = 42;

    // other things

    return Ext.Function.bind(fn, null, [x]); // o is not captured
}

var f = runAsync(1);

持续创建具有副作用的实例

创建某些对象可能会产生副作用(例如,创建 DOM 元素)。如果创建这些对象而没有销毁它们,则它们可能会泄漏内存。

{
    xtype: 'treepanel',
    listeners: {
        itemclick: function(view, record, item, index, e) {

            // Always creating and rendering a new menu
            new Ext.menu.Menu({
                items: [record.get('name')]
            }).showAt(e.getXY());
        }
    }
}

解决方案: 捕获对菜单的引用,并在不再需要它时在其上调用 destroy 方法。

清除缓存中的任何注册

删除对对象的所有引用非常重要。将本地引用设置为 null 是不够的。如果某些全局单例缓存正在持有引用,则该引用将在应用程序的生命周期内被持有。

var o = new SomeLargeObject();
someCache.register(o);

// Destroy and null the reference. someCache still has a reference
o.destroy();
o = null;

解决方案: 除了调用 destroy 之外,还要确保从已添加对象的任何缓存中删除对象。

总结

掌控应用程序的内存管理可以是一项简单的任务。通过销毁未使用的组件、使未使用的引用无效以及使用 callParent(),使您的应用程序无可指摘。遵循这些建议将确保您的应用程序平稳运行,并且不会不负责任地使用资源。

Ext JS 7.8.0