具有深度嵌套集合的 Angular 性能和数据绑定

angular performance and data binding with a deeply nested collection

本文关键字:Angular 性能 数据绑定 集合 深度 嵌套      更新时间:2023-09-26

我正在使用chrome.bookmarks API为Chrome制作一个书签管理器。

对于我的概念证明,我的书签页面是我所有书签的单个列表。我正在使用 2 个指令,两个遍历我的书签的整个集合,一个collection directive和一个member directive。我遇到了性能问题,我正在寻找方向。

博士: 模型更改需要永远更新我的观点,我做错了什么,我怎样才能做得更好?

以下是相关代码:

app.controller('bookmarksController', ['$scope', 'ChromeBookmarks', function($scope, chromeBookmarks) {
  chromeBookmarks.getBookmarks().then(function(bookmarkTree) {
    $scope.bookmarks = bookmarkTree;
  });
});

索引.html

<div ng-controller="bookmarkController"> 
  <collection collection="bookmarks"></collection>
</div>

收藏.js

app.directive('collection', function($compile) {
  return {
    restrict: 'E',
    scope: {
      collection: '=',
    },
    templateUrl: 'collection.html',
  };
});

收藏.html

<ul>
  <member ng-repeat="member in collection" member="member"></member>
</ul>

成员.js

app.directive('member', ['$compile', function($compile) {
  return {
    restrict: 'E',
    scope: {
      member: '=',
    },
    templateUrl: 'member.html',
    link: function(scope, element, attrs) {
      if (scope.member.children) {
        var collectionTemplate = '<collection collection="member.children"></collection>';
        var collection = angular.element(collectionTemplate);
        element.append(collection); 
        $compile(collection)(scope);
      }      
    }
  };
}]);

成员.html

<li>
  <bookmark ng-if="member.url" node="member"></bookmark>
  <folder ng-if="!member.url" node="member"></folder>
</li>

书签.js

app.directive('bookmark', function() {
  return {
    restrict: 'E',
    scope: {
      node: '='          
    },
    templateUrl: 'bookmark.html'
  };
});

书签.html

<div>
  {{ node.title }} <button ng-click="open()">Live Site</button>
</div>

文件夹.js

app.directive('folder', function() {
  return {
    restrict: 'E',
    scope: {
      node: '='
    },
    templateUrl: 'folder.html'
  };
});

文件夹.html

<div> {{ node.title }} </div>

正如您在上面的控制器中看到的那样,我只有一个模型附加到示波器:$scope.bookmarks .我只想加载这个变量一次。

页面加载后,我想将侦听器附加到 chrome.bookmark 事件,以便在书签更改时触发事件,我只需要更改受影响的书签。

我目前执行此操作的方法是使用一个函数,该函数获取更改的书签并递归地在$scope.bookmarks中移动,并将书签的父项替换为我从chrome.bookmarks API获得的更新版本。该函数基本上执行以下操作: $scope.bookmarks[1].children[2] = updatedBookmark

我尝试将侦听器添加到我的页面,但是创建书签并随后更新视图需要 3 秒(不好),如果我为窗口中的每个选项卡添加书签,页面会冻结(更糟)。我确实有大约 2k 个书签,但我仍然希望它的性能超出该级别。

我的理解是,糟糕的角度表现与拥有大量的观察者有关,因为$digest周期变得太大。这是您认为导致我性能问题的原因吗?

如果我只想修改$scope.bookmarks模型以触发视图更新,我真正需要多少个观察者?

我知道 2 路数据绑定旨在保持模型和视图同步,但我不禁想到,就我而言,我只想更改一点数据(并且更改基于书签 API 事件,不一定是用户输入),2 路数据绑定似乎是错误的, 特别是因为$digest循环似乎检查了如此多的数据。

但话又说回来,我确实需要视图来正确镜像模型数据。如果我摆脱了 2 路数据绑定,并使用 ngOnce 进行一次性绑定,是否可以呈现对模型的更改?

我是否只是遇到了角度的自然限制并需要使用不同的框架?

Angular 不能处理超过 ~2000 个手表,一些浏览器可以做更多,但这是一个很好的上限。缓慢更新有两种可能性:

1) 你在 Angular 生命周期中调用的 API 是否在 Angular 生命周期内?如果是这样,请确保执行 scope.$apply()。2)你的手表太多了。您可以在控制台中找到一些代码片段,这些代码片段将吐回监视的数量 如果它们很高,请考虑缓冲您的视图,以便只有您看到的内容被绑定,github.com/EnzeyNet/VirtualScroll。