Показать сообщение отдельно
  #5 (permalink)  
Старый 12.07.2014, 13:39
Профессор
Отправить личное сообщение для DjDiablo Посмотреть профиль Найти все сообщения от DjDiablo
 
Регистрация: 04.02.2011
Сообщений: 1,815

Я тут поразмышлял на досуге на твоей задачей и запилил с нуля эксперимент. В принципе сейчас в хроме скролятся 40 000 директив с таймерами без каких либо ощутимых проблем

вот что получилось.

1) Во первых я прикинул что если я скрою 20000 элементов какой либо директивой типо ng-show то у меня будет 20 000 watch которые будут наблюдать за тем не изменилось ли значение в ng-show, а это гарантированные тормоза. Такая перспектива меня совсем не улыбала и Я реализовал подход в котором добавляются и компилируются только элементы которые были невидимы но стали видимы после смещения скролла, элементы выходящие за границы видимости удаляются. А те элементы которые были видимы раньше и остались видимы после смещения скролла ни как не изменяются. То есть я минимизировал количество манипуляций с DOM и компиляций выполнив их все вручную отказавшись от использования шаблонизатора.

2) Шага один оказалось недостаточно и на большом количестве элементов появились тормоза. Как выяснилось после удаления остался работать таймер в удаленных директивах (о чем кстатии недвухсмысленно предупреждают в документации). Встал вопрос о том как убить таймер. В итоге каждую директиву я научил определять не вышла ли она за границы видимой зоны и если вышла то останавливать таймер и все watch. Я хотел сделать чтобы при выходе за границы директива еще сама и себя удаляла, но watch в директиве работает с опозданием и директивы при скролинге не успевают удаляться вовремя.


Удаление и вставка элементов вероятно является не обязательной и достаточно будет просто чтобы скрытый элемент заглушил таймер и все watch, напротив при появлении в видимой зоне все таймеры и наблюдатели должны ожить. Тобишь функция которая оживит watch и таймеры должна быть запущена infinityScroll'ом но ни как не watche'ром в самой директиве ибо мы опять получим тысячи тормозных watch.

Резюмируя результаты эксперимента могу сказать что
1) ключом к успеху является отписывание от событий/таймеров/наблюдений элементов вышедших за пределы видимой зоны. То есть это задача больше вставленной в infinityScroll директивы чем самого infinityScroll.
2) Еще важный момент в том чтобы не допустить в шаблонизаторе десятков тысяч одновременно работающих ng-if/ng-show и т.п. поскольку каждая такая директива использует дорогой $scope.$watch.
3) Следует избегать полной перегенерации dom при небольшом смещении скролла (но это не самый критичный момент)

Пощупать опыт можно здесь.
для генерации кучи записей кликните на скролл и удерживайте pageDown
<iframe width="100%" height="750" src="http://jsfiddle.net/5hAy2/3/embedded/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>


В данный момент снижается гладкость скроллинга при очень большом количестве записей. Это связано с перебором массива items целиком но это можно исправить через оптимизацию поиска в массиве items элементов которые должны быть видимы , удаляемые же элементы можно найти просто перебрав кэш.

P.S. Код эксперементальный на скорую руку как следствие грязный но надеюсь что этого будет достаточно чтобы понять смысл.

P.P.S Потом если будет время то я еще по экспериментирую с шаблонизатором. Думаю если написать умный аналог ng-repeat который возьмет на себя задачу с минимизацией манипуляциями dom то этого будет вполне достаточно чтобы заменить Render и RenderItem шаблонизацией. В принципе на умный ng-repeat можно повесить и задачу информирования удаляемых элементов об их удалении (если не найду другой приемлемый способ(UPD: уже найден)) чтобы те могли остановить таймеры. Вместо удаления также можно попробовать сваять умный ng-repeat заточенный под скрытие элементов вместо удаления и информирующий детей о скрытии и наоборот о появлении что те могли остановить таймер. Нужно еще попробывать запись item in items track by $id(item.id) чото я ее непомню может ее раньше небыло. В общем пока что я пребываю в размышлениях обо всем этом.

P.P.P.S пожалуй стоит еще с $scope.$on('$destroy',function(){}); поэксперементировать. (UPD: выбрал element.$on("$destroy",..))
__________________
Лучше калымить в гандурасе чем гандурасить на колыме

Последний раз редактировалось DjDiablo, 12.07.2014 в 18:15.
Ответить с цитированием