AngularJS的性能问题

时间:2014-02-28 03:57:15

标签: javascript html performance angularjs lag

我正在构建一个复杂的角度应用程序,它具有无限滚动,具有类似马赛的列和图像。我做了一项研究,并编写了自己的指令来为我呈现数据:

angular.module('deepsy.flexgrid', []);

angular.module('deepsy.flexgrid').directive('flexgrid', [

    'flexgridFactory',

    function initialize(flexgridFactory) {
        return flexgridFactory.create();
    }
]);


angular.module('deepsy.flexgrid').factory('flexgridFactory', [

    'FlexGrid',
    '$log',

    function initialize(FlexGrid, $log) {

        function Creator() {
            this.restrict = 'AE';

            this.scope = {
                'model': '=source',
                'opts': '=options'
            };

            this.$$grid = null;

            this.link = this.$$link.bind(this);
        }

        Creator.prototype.$$link = function $$link(scope, elem, attrs) {
            scope.itemTemplate = attrs.template;

            scope.mother = scope.$parent;

            scope.template = elem.html();
            elem.html('');

            this.$$grid = new FlexGrid(scope, elem);
        };

        return {
            create: function create() {
                return new Creator();
            }
        };
    }
]);

angular.module('deepsy.flexgrid').factory('FlexGrid', [

    '$compile',

    function($compile) {

        function FlexGrid(scope, elem) {
            this.scope = scope;
            this.elem = elem;
            this.state = null;
            this.counter = 0;
            this.id = Math.floor(Math.random() * 999);

            this.$getColumns();
            this.$createColumns();
            window.onresize = this.$watchSize.bind(this);


            this.scope.$watch(function() {
                return this.scope.model;
            }.bind(this), this.$applyData.bind(this), true);
        }

        FlexGrid.prototype.$getColumns = function() {
            var curr = null,
                width = document.body.clientWidth,
                min, max;

            for (var i in this.scope.opts.columns) {
                curr = this.scope.opts.columns[i];
                min = curr.min || 0;
                max = curr.max || 99999;

                if (min < width && width < max) {
                    this.state = curr;
                    return curr;
                }
            }
        };

        FlexGrid.prototype.$createColumns = function() {
            var output = [];
            for (var i = 0; i < this.state.columns; i++) {
                output.push('<div id="flexgrid_' + this.id + '_' + i + '" class="gridColumns ' + this.state.class + '"></div>');
            }
            this.elem.html(output.join(''));
        };

        FlexGrid.prototype.$watchSize = function() {
            var curr = this.state || {
                columns: 0
            };
            this.$getColumns();
            if (this.state.columns != curr.columns) {
                this.$createColumns();
                this.$fillData(0);
            }
        };

        FlexGrid.prototype.$applyData = function(newVal, oldVal) {

            var bindings = [],
                count = 0;

            oldVal.forEach(function(obj) {
                bindings.push(obj._id);
            });

            newVal.forEach(function(obj) {
                if (bindings.indexOf(obj._id) != -1) {
                    count++;
                }
            });

            if (count == oldVal.length && oldVal.length > 0) {
                this.$fillData(count);
                //  console.log('add');
            } else {
                this.$fillData(0);
                //  console.log('render');
            }
        };

        FlexGrid.prototype.$fillData = function(start) {
            var columns = this.state.columns,
                len = this.scope.model.length;

            if (start === 0) {
                this.$clearColumns(columns);
                this.counter = 0;
            }

            for (var i = start; i < len; i++) {
                $("#flexgrid_" + this.id + "_" + this.counter).append(this.$compile(this.scope.model[i]));

                if (this.counter++ === columns - 1)
                    this.counter = 0;
            }

            //$("img", this.elem).load(function(){
            //  $(this).parent().fadeIn(700);
            //});
        };

        FlexGrid.prototype.$compile = function(data) {
            var compiled = this.scope.template.replace(/\{\|[^\|\}]*\|\}/gmi, function(exp, val) {
                return data[exp.replace(/\|\}|\{\|/gmi, '')];
            });

            compiled = compiled.replace('src-image', 'src="' + data.image + '" dinimg');

            return compiled;
        };

        FlexGrid.prototype.$clearColumns = function(columns) {
            for (var j = 0; j < columns; j++) {
                $("#flexgrid_" + this.id + "_" + j).empty();
            }
        };

        return FlexGrid;
    }
]);

它工作得很棒,但有一些性能问题。我首先喜欢“嗯......我可能有太多观察者”,但后来我优化了我的代码,并在浏览器控制台中执行以下代码后:

(function () { 
var root = $(document.getElementsByTagName('body'));
var watchers = [];

var f = function (element) {
    if (element.data().hasOwnProperty('$scope')) {
        angular.forEach(element.data().$scope.$$watchers, function (watcher) {
            watchers.push(watcher);
        });
    }

    angular.forEach(element.children(), function (childElement) {
        f($(childElement));
    });
};

f(root);

console.log(watchers.length);
})();

我发现我只有一个观察者!所以观察者可能不是问题所在。使用此代码,我实现了无限滚动。每个元素都包含图像和文本。问题是,在获得超过200-250个项目后,我的UI开始滞后,即使我有1个观察者。我认为这可能是因为图像尺寸,但在我在Chrome控制台$(".item").html('')中执行并清除了所有框的内容后,我的UI仍然不顺畅。这个问题不是由太多的DOM元素引起的,因为除了指令所呈现的div之外,我总共只有38个div。如何提高我的应用程序的性能?

编辑:还有一个奇怪的事情,我注意到即使我通过$(".item").remove从DOM中移除所有元素(在渲染之后),浏览器仍然使用300mb + RAM。

1 个答案:

答案 0 :(得分:2)

我会使用 AngularJS Batarang 插件仔细检查观察者的数量,该插件有一个“性能”标签,您可以在其中查看所有手表。


以下是如何使用chrome dev工具分析内存的摘要。

堆分配配置文件:记录随时间的分配

  • 打开chrome dev工具
  • 打开个人资料标签
  • 点击堆分配资料
  • 点击开始
  • 执行将数据加载到网格中所需的内容
  • 点击停止

堆快照:记录实际分配的对象

  • 打开chrome dev工具
  • 打开个人资料标签
  • 点击堆快照
  • 点击拍摄快照
  • 执行将数据加载到网格中所需的内容
  • 在左上角再次点击个人资料
  • 点击拍摄快照
  • 运行$(".item").html('')
  • 在左上角再次点击个人资料
  • 点击拍摄快照
  • 点击左侧3个配置文件中的一个,查看实例

你也可以:

  • 点击左侧3个个人资料中的一个
  • 现在在顶部栏中将下拉列表设置为“comparaison”
  • 在“比较”选项旁边,选择要比较的快照。