【问题标题】:Angular ng-repeat cache (avoid re-rendering on state change)Angular ng-repeat 缓存(避免在状态更改时重新渲染)
【发布时间】:2015-09-08 07:03:02
【问题描述】:

我们在 Angular 应用程序中使用 ng-repeat 出现了巨大的渲染峰值。 主页显示大量封面图片(“::”和“track by”已到位)。 首次加载时,它可以正常工作。

但如果用户更改状态(我们使用 UI-Router)并随后返回主页,则在桌面上延迟大约 2 秒,在移动设备上延迟最多 10 秒。

它应该是即时的:所有 json 查询都被缓存。并且 ng-repeat 已经渲染了该内容一次。

作为临时解决方案,我们使用 angular ux datagrid (https://github.com/obogo/ux-angularjs-datagrid) 它可以立即返回首页,但它不应该在水平模式下工作。而且使用专用网格来缓存 ng-repeat(或它在幕后所做的任何事情)似乎有点过头了。

所以问题如下:如何避免 ng-repeat 在状态更改时重新渲染内容?

【问题讨论】:

  • 使用缓存的共享服务
  • 缓存什么?所有 Json 都使用 $cacheFactory 进行缓存。问题在于渲染
  • 您是否正在压缩服务器上的图像?他们多大?你用css调整它们的大小吗?您可以缓存您认为需要缓存的任何内容。老实说,它听起来像是和图像的东西,如果你能做一个小提琴或其他东西来展示它来修补它会很酷。可以帮助您轻松很多。
  • 如果页面根本不需要重新渲染,你可以缓存 dom,使用指令。

标签: javascript angularjs performance caching angularjs-ng-repeat


【解决方案1】:

好吧,如果您禁用 ng-repeat 所在范围的范围。然后它将不再渲染。它本质上变成了静态内容。这使您可以实际控制渲染的时间。

ux-datagrid 实际上使用这个概念来关闭视野之外的 dom,因此 angular 不知道它并且无法渲染它。然后,当它在视野中时,它会重新连接它。

每个作用域都在一个摘要循环中工作。在摘要循环中,它处理范围内的 $watchers。

如果你删除了这些观察者,它不会消化它或者它是孩子。

这些是 ux-datagrid 在其代码中用于激活和停用作用域的 2 种方法。您可以将它们复制到另一个对象并将它们用于相同的事情。

/**
 * ###<a name="deactivateScope">deactivateScope</a>###
 * One of the core features to the datagrid's performance is the ability to make only the scopes
 * that are in view to render. This deactivates a scope by removing its $$watchers that angular
 * uses to know that it needs to digest. Thus inactivating the row. We also remove all watchers from
 * child scopes recursively storing them on each child in a separate variable to activate later.
 * They need to be reactivated before being destroyed for proper cleanup.
 * $$childHead and $$nextSibling variables are also updated for angular so that it will not even iterate
 * over a scope that is deactivated. It becomes completely hidden from the digest.
 * @param {Scope} s
 * @param {number} index
 * @returns {boolean}
 */
function deactivateScope(s, index) {
    // if the scope is not created yet. just skip.
    if (s && !isActive(s)) { // do not deactivate one that is already deactivated.
        s.$emit(exports.datagrid.events.ON_BEFORE_ROW_DEACTIVATE);
        s.$$$watchers = s.$$watchers;
        s.$$watchers = [];
        s.$$$listenerCount = s.$$listenerCount;
        s.$$listenerCount = angular.copy(s.$$$listenerCount);
        subtractEvents(s, s.$$$listenerCount);
        if (index >= 0) {
            s.$$nextSibling = null;
            s.$$prevSibling = null;
        }
        return true;
    }
    return false;
}

/**
 * ###<a name="activateScope">activateScope</a>###
 * Taking a scope that is deactivated the watchers that it did have are now stored on $$$watchers and
 * can be put back to $$watchers so angular will pick up this scope on a digest. This is done recursively
 * though child scopes as well to activate them. It also updates the linking $$childHead and $$nextSiblings
 * to fully make sure the scope is as if it was before it was deactivated.
 * @param {Scope} s
 * @param {number} index
 * @returns {boolean}
 */
function activateScope(s, index) {
    if (s && s.$$$watchers) { // do not activate one that is already active.
        s.$$watchers = s.$$$watchers;
        delete s.$$$watchers;
        addEvents(s, s.$$$listenerCount);
        delete s.$$$listenerCount;
        if (index >= 0) {
            s.$$nextSibling = scopes[index + 1];
            s.$$prevSibling = scopes[index - 1];
            s.$parent = scope;
        }
        s.$emit(exports.datagrid.events.ON_AFTER_ROW_ACTIVATE);
        return true;
    }
    return !!(s && !s.$$$watchers); // if it is active or not.
}

我不确定这是否能完全回答您的问题,因为您使用的是 UI-Router。如果视图被重新创建并且没有被缓存,那么它仍然会在编译中重新渲染它。但是,如果不是这样,这不仅会监视一次,当您禁用此范围时,它也会禁用该范围的所有子级。基本上将它与摘要和所有子节点分离。

重新启用它会将它们全部重新添加。因此,您只需一次调用即可关闭 ng-repeat 及其中的所有内容。在您重新启用它之前,它一直是静态的。

【讨论】:

  • 在深入挖掘 Angular UI-Router 后,我发现:github.com/angular-ui/ui-router/issues/63 这正是我面临的问题。如果我只渲染几个封面,禁用范围并在之后启用它,那么 Wes 解决方案会很好。
猜你喜欢
  • 2020-05-04
  • 1970-01-01
  • 1970-01-01
  • 2022-06-29
  • 2014-01-16
  • 2021-02-20
  • 1970-01-01
  • 1970-01-01
  • 2014-06-03
相关资源
最近更新 更多