【问题标题】:Directive causing infinite digest loop导致无限摘要循环的指令
【发布时间】:2026-02-17 13:10:02
【问题描述】:

以下指令导致无限摘要循环,我不知道为什么。关于如何重新编写代码的任何想法?

谢谢!

  .directive('fitHeight', ['$window', '$timeout', '$location', function ($window, $timeout, $location) {
return {
  restrict: 'A',
  scope: true,
  link: function (scope, element, attr) {
    scope.docHeight = $(document).height();
    var setHeight = function (newVal) {
      var diff = $('header').height();
      if ($('body').hasClass('layout-horizontal')) diff += 112;
      if ((newVal-diff)>element.outerHeight()) {
        element.css('min-height', (newVal-diff)+'px');
      } else {
        element.css('min-height', $(window).height()-diff);
      }
    };
    scope.$watch('docHeight', function (newVal, oldVal) {
      setHeight(newVal);
    });
    $(window).on('resize', function () {
      setHeight($(document).height());
    });
    var resetHeight = function () {
        scope.docHeight = $(document).height();
        $timeout(resetHeight, 1000);
      }
    $timeout(resetHeight , 1000);
  }
};

【问题讨论】:

    标签: angularjs angularjs-directive angularjs-digest


    【解决方案1】:

    您的$timeout(resetHeight, 1000) 导致Infinite $digest loop。这是因为方法resetHeight 正在更改变量$scope.docHeight 并开始另一个$timeout 循环,$timeout 触发$scope.$apply()。 Angular 在内部监视可能出现或实际存在的情况,并且无穷无尽的$digest loop,如果发现这种情况,则会引发异常。

    我必须做一些类似于你正在做的事情,我不会在 Window Resize 事件上做 Debouncing 逻辑。这是因为在调整窗口大小时会触发该事件。你需要throttle处理程序调用,这样你就不会有$digest loops

    在下面的示例中,我将 Lodash 用于我的去抖动逻辑。

    http://plnkr.co/edit/vUHcgpveDvijGE6l1LXy?p=preview

                angular.module('myApp', [])
                    .directive('fitHeight', ['$window', '$timeout', '$location', function ($window, $timeout, $location) {
                        return {
                            restrict: 'A',
                            scope: true,
                            link: function (scope, element, attr) {
                                function setHeight(newVal) {
                                    var diff = $('header').height();
    
                                    if ($('body').hasClass('layout-horizontal')) {
                                        diff += 112;
                                    }
    
                                    if ((newVal - diff) > element.outerHeight()) {
                                        element.css('min-height', (newVal - diff) + 'px');
                                        console.log('if');
                                    } else {
                                        element.css('min-height', $(window).height() - diff);
                                        console.log('else');
                                    }
                                }
    
                                setHeight($(document).height());
    
                                var docHeight = $(document).height();
                                var heightCheck = function() {
                                    if (docHeight != $(document).height()) {
                                        docHeight = $(document).height();
    
                                        setHeight(docHeight);
                                    }
                                };
    
                                var lazyHeightCheck = _.debounce(heightCheck, 200);
                                $(window).on('resize', lazyHeightCheck);
    
                                element.on('$destroy', function() {
                                    $(window).off('resize', lazyHeightCheck);
                                });
                            }
                        }
                    }]);
    

    【讨论】:

    • 感谢您的代码。但是,当页面首次加载时,不会发生调整大小。
    • @Marco 对不起,我忘了打第一个电话,我修复了示例并添加了一个实时示例供您测试。
    • 我也在玩去抖动间隔,0 也可以。这能够避免任何屏幕闪烁
    • 恕我直言,但我想答案是错误的。 $timeout(resetHeight, 1000) 不会递归调用resetHeight,而是将其注册为在一秒钟内调用。这是每秒运行一次代码的常用方法。我不确定是什么触发了 OP 中的循环,但这个 plnkr 可能更符合原始代码。
    【解决方案2】:

    您在 resetHeight 函数内部有一个无限循环调用 resetHeight 函数。无限的 $digest 循环是无限循环的副作用。

    【讨论】: