Список оптимизаций рендеринга DOM, реализуемых на уровне Javascript фреймворка

    С октября 2009 года я занимаюсь разработкой приложения для поиска и прослушивания музыки. Я стремлюсь организовать возможность быстрого взаимодействия пользователя с интерфейсом, и в качестве одного из средств ускорения взаимодействия использую различные способы для быстрой отрисовки страниц.

    Предлагаю ознакомиться со способами, реализованными мной в приложении на системном уровне:

    • Использование CSS и переключение классов вместо перестроения DOM дерева
    • Повсемнестное кеширование выборок элементов ($('div.active_part span.highlighter')), атомарные операции по изменению (вместо всеобщей перерисовки, вместо переделывания больших участков DOM дерева)
    • Минимизации чтений DOM во время записи изменений состояний
    • Кеширование размеров и расположения элементов (это спасает от излишнего вычисления при считывании этих значений при наличии других изменений: чтение во время изменения множества частей дерева крайне негативно сказывается на производительности)
    • Аккуратное, не затягивающееся накопление изменений, необходимых произвести в DOM
    • Прикрепление частей изменяющихся коллекций единовременно (когда, например, в середину списка вставляется 3 новых элемента; createDocumentFragment) в конкретное место (after, before) вместо открепления всей коллекции от DOM и повторного прикрепления (и вместо того, чтобы перерисовывать весь список)
    • Прогрессивный асинхронный рендеринг: картина прорисовывается сразу с небольшим количеством деталей, затем деталей появляется всё больше
    • Клонирование нодов (как часть шаблонизации)
    • Кеширование и использование кеша результатов парсинга DOM шаблонов


    image image
    изображения из части с заголовком «Прикрепление частей изменяющихся коллекций единовременно...»


    Использование CSS и переключение классов вместо перестроения DOM дерева



    Принцип, согласно которому максимум визуальной части переносится в CSS, а изменение отображаемого происходит за счёт переключения классов (например, переключение класса .hidden { display:none } вместо удаления/создания нода если нужно просто скрыть элемент). В то же время использование данного принципа позволяет не перегружать браузер чрезмерным количеством DOM нодов в документе.


    в этом примере для отображения полного списка у одного из нодов просто переключается класс want_more_songs



    Повсеместное кеширование выборок нодов, атомарные изменения



    Под атомарными изменения я подразумеваю принцип, когда вместо того, чтобы полностью переделывать, заново парсить и строить новые большие части DOM структуры, мы изменяем какую-то конкретную часть уже существующего DOM. Причем изменения структуры привязаны к изменениям состояний как можно конкретней. Если должен измениться класс, — меняем через $('.target_node').addClass('active'), если текст, — то через $('.target_node').text('сделать заказ'). Это же самое касается атрибутов и других частей DOM.

    Это первые оптимизации, один из первых принципов, который я использовал (в связи с чем отказался от шаблониторов, особенно в стиле underscorejs.template). Они заклчаются в том, чтобы при изменениях состояний модели не перерисовывать всю вьюху, а изменять лишь часть. Согласно им я не должен использовать .innerHTML (или jquery(...).html()), а также должен кэшировать выборки нодов (селекторов), т.е. если мне нужно изменить какую-либо часть, то я не делаю выборку каждый раз, а просто беру нужный нод из кеша.
    Эти две вещи организованы у меня так: код разделён согласно MVC, при изменениях состояний модели информация об изменениях отправляется во вьюхи, во вьюхе срабатывает функция, связанная с изменяемым состоянием. Функция использует кеширование для выборок.
    Например, у модели были следующие состояния:
    {
      artist: "The Killers",
      track: null
    }

    мы изменили их этим кодом:
    track_model.updateState('track', "When You Were Young");

    состояния стали такими:
    {
      artist: "The Killers",
      track: "When You Were Young"
    }

    состояние поля track изменилось, и мы отправили эти изменения из модели во вьюху.


    Анимация, 3 кадра. Изменяем в модели состояние track, 2 вьюхи мгновенно реагируют на изменение

    во View срабатывает функция, реагирующая на изменение какого-то конкретного состояния и производящая DOM изменения, в данном случае сработает следующий код:

    'stch-track': function(new_state, old_state){
      this.container
        .find('.song-track-name')
        .text(new_state)
        .toggleClass('hidden', !new_state);
    }

    (обратите внимание на название метода вида 'stch-' + state_name, в данном случае 'stch-track');
    так конкретные изменения привязаны к конкретным частям DOM

    Теперь обратим внимание на кеширование. Если при каждом изменении делать выборку (this.container.find('.song-track-name')), то это негативно скажется на производительности, поэтому, чтобы не делать каждый раз выборку, сразу меняем например на такой код:
    'stch-track': function(new_state, old_state){
      if (!this.track_name_node){
        this.track_name_node = this.container.find('.song-track-name'); 
        // этот же код можно было бы вынести в инициализацию нашей вьюхи
      }
      this.track_name_node.text(new_state).toggleClass('hidden', !new_state);
    }

    (так я кешировал выборки, когда только начинал писать код, в соответствии с принципами атомарных изменений)

    Позже, когда я начал делать и использовать свой шаблонизатор, для случаев, когда необходимо произвести измения в DOM в ручном режиме, я создал дирeктиву (<span pv-anchor=«track_name_con» class=«song-track-name»></span>), которая указывает, что нод нужно закешировать в хранилище, потому что я буду обращаться к нему вручную (вместо того, чтобы каждый раз писать императивный код this.container.find('.song-track-name')). Таким образом, я могу переписать реакцию на изменения так:

    'stch-track': function(new_state, old_state){
      this.tpl.ancs['track_name_con'].text(new_state).toggleClass('hidden', !new_state);
    }

    Такие простые изменения (когда от состояния зависит текст или класс, или какой-либо иной атрибут) обычно описываются другими директивами, а директива pv-anchor использутеся для более сложных изменений (например затрагивающих расположение или размер других нодов), для навешивания событий при сложном взаимодействии и для других случаев, где DOM меняется вручную.

    Минимизации чтений DOM во время записи изменений состояний



    Браузер старается произвести рендеринг после того, как были произведены все изменения в DOM. Однако ему придётся вычислить всё гараздо раньше, а потом ещё раз, если вы попытаетесь считать некоторые свойства. Поэтому я стремлюсь считывать DOM как можно реже, а если не получается, то кешировать (если возможно) результаты чтения.


    Сейчас существуют быстродейственные и относительно удобные шаблонизаторы, например, шаблонизатор в angularjs и Facebook React. Когда я был в самом начале пути, я не знал о существовании удобных шаблонизаторов, делающих эти простые вещи (они тогда вообще были?), поэтому я и не использовал их.
    Когда код в приложении эволюционировал, разделился по принципу MVC, у меня по-прежнему не было шаблонизатора, поэтому все реакции на изменения я описывал вручную (как это описано в примере выше).

    Позже я обратил внимание на шаблонизатор в быстро набирающем популярность angularjs, который также придерживался принципа атомарных изменений (и имел data binding). Но интерес для меня представлял скорее принцип, согласно которому изменения состояний привязываются к свойствам элементов. Это происходит при помощи декларирования дополнительных аттрибутов в html документе.

    Я взял из angularjs код, отвечающий за парсинг выражений, и сам декларативный способ дата байндинга через атрибуты. Такой способ описания связи оставляет HTML код работающим и позволяет выполнить браузеру самую сложную часть работы (HTML парсинг), а потом многократно использовать результат (с помощью клонирования методом .cloneNode().

    Пример:
    <div
      class="user_tag_page usual_page"
      pv-class="lula_page usual_page {{!vmp_show && 'hidden'}}"
    ></div>


    В данном примере используется директива pv-class. Согласно её значению, у элемента всегда будут классы lula_page usual_page, а также, если у модели нет состояния vmp_show, то у элемента должен быть ещё один класс — «hidden». Шаблонизатор будет перезаписывать .className автоматически при изменении состояния vmp_show, однако, если шаблонизатор не используется, то className не будет перезаписан и его значение будет таким, каким мы записали его изначально, — class=«user_tag_page usual_page».

    Такой способ не только удобен и даёт возможность оптимизаций парсинга, но и даёт потенциал по оптимизациям производительности, связанных с внесением изменений в DOM. Например, оптимизация производительности реализуется при изменении классов элементов. Возможно, вы знаете, что метод jQuery(node).addClass('sample-class') прежде, чем добавить класс, проверяет, есть ли такой класс у элемента, т.е. прежде, чем произвести запись, делает дополнительное чтение (по крайне мере до появления classList api). Но т.к. в шаблонизаторе мы знаем полный набор классов и набор классов, который должен быть при текущем состоянии модели (например "lula_page usual_page hidden"), мы можем сразу сделать так:

    node.className = "lula_page usual_page hidden";

    т.е. нам совсем не нужно было читать предыдущее значение className, потому что мы меняем его только, если состояние изменилось, и мы знаем весь список состояний, от которых он зависит, и каким конкретно должно быть значение класса.

    Зная состояния моделей и сооствествующую им DOM структуру, читать этот самый DOM вообще не нужно (если только ничего не зависит от размеров каких-либо других частей).

    Впрочем, конкретно с чтением классов, описанный принцип может вообще не давать прироста в производительности (по крайне мере, современный браузер должен понимать, что ему не нужно вычислять какие-либо размеры, расположения для того, чтобы предоставлять полное значение className), но это маленький бонус, получаемый мной от шаблозиратора.

    Однако, в записи, связанной с выходом последней, на текущий момент, вышедшей версии jquery (1.11, 2.1, 24 января 2014) разработкичи сообщили (см. под заголовком Fewer forced layouts), что исправили лишние срабатывания layout change при изменении класса. Т.е. это имеет значение, причем достаточно серьёзное. Разработчики хрома тоже работают над этой проблемой.

    Кеширование размеров и расположения элементов



    Считывание размеров или расположения элементов вызывает преждевременное вычисление всех других произведённых изменений — это удачное место для оптимизаций. Для случаев, где это возможно, я использую кеширование.

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


    обычная ширина прогресс бара файла


    ширина прогресс бара при развернутом списке файлов


    ширина прогресс бара при другой ширине окна



    Для вычисления необходимо знать ширину родительского элемента. Ширина родительского элемента может варьироваться от ширины экрана и полного или одиночного отображения списка найденных файлов.

    В данном случае ни от каких других параметров ширина не зависит. При одинаковой ширине и режиме отображения списка ширина нода будет всегда одинаковая. Я использую состояния, которые могут влиять на ширину как часть ключа при доступе в хранилище.

    this.getBoxDemension(this.getProgressWidth, 'progress_c-width', window_width, !!p_wmss);


    Первый параметр метода (getProgressWidth) — это просто функция, которую метод getBoxDemension сам вызовет, если в хранилище нет значения для данного ключа.
    Все остальные параметры просто складываются в строку. В данном случае p_wmss в строке станет ‘false’ или ‘true’ (отображется только один файл или весь список). В результате ключ может выглядеть например так progress_c-width-1372-false.

    При изменении ширины окна или режима отображения будет вычислена новая ширина для нового ключа (если такой нет ещё в хранилище), а зависящие от этой ширины части будут автоматически перерисованы.

    На самом деле, к ключу каждый раз добавляется ещё две строки — это имя позиции гнезда-слота данной вьюхи внутри родительской вьюхи (в терминологии моего фреймворка, который автоматически присвает это имя позиции вьюхам), и то же самое верно и для родительской вьюхи внутри её родительской вьюхи. Это используются для случаев, когда одна и та же вьюха используется в разных местах, в связи с чем вычисляемые размеры зависят не только от, например, ширины экрана и режима отображения списка файлов, но и от того, где используется вьюха.

    Дополнительно вы можете ознакомиться с видеозаписью, где в части своего доклада об этом рассказывает сотрудник компании гугл Пол Льюис. См. также совместную статью Пола Люиса и Пола Айриша, на эту же тему. Список свойств, так или иначе влияющих на рендеринг из той статьи.

    Аккуратное, не затягивающееся накопление изменений необходимых произвести в DOM



    В фреймворке реализован сбор всех состояний в пакет до отправки во вьюхи для состояний модели, зависящих от иного состояния и изменяющихся при обновлении этого состояния. Но только у этой модели, а не у всего приложения.

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

    Таким образом, реализовано накопление изменений состояний, не затягивающееся на чересчур долгое время.

    Чтобы рендеринг происходил в одну итерацию при ручном обновлении нескольких состояний, я пользуюсь следующим методом:
    .updateManyStates({
      artist: 'artist_224',
      title: 'radnomd title 014'
    })
    


    вместо того, чтобы выполнить два таких шага:
    updateState('artist', 'artist_224')

    updateState('title', 'radnomd title 014')


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

    Прикрепление частей изменяющихся коллекций единовременно (createDocumentFragment) в конкретное место (after, before) вместо открепления всей коллекции от DOM и повторного прикрепления



    При изменении списков список не перерисовывается вновь.
    При изменении списков части DOM, связанные с элементами списка, не создаются заново.
    При изменении списков новая очерёдность DOM элементов списка устанавливается не откреплением полностью от документа, а откреплением, если необходимо, и прикреплением изменивших свою позицию (или новых) элементов в строго определенное для них место.
    DOM, соответствующий элементам списка, которые нужно прикрепить, прикрепляется не по отдельности, а с помощью createDocumentFragment, если элементы идут друг за другом. Элементы прикрепляются к documentDrafment в нужном порядке, потом documentDrafment прикрепляется к документу в нужное место с помощью методов after, before или append.

    Таким образом, при изменении списка в DOM происходят только самые необходимые и минимальные изменения.


    список с подсвеченным последним элементом этого списка (глобальная переменная $0 в консоли — это выбранный и подсвеченный элемент, $1, $2, $3 — элементы, выбранные до этого)


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



    Дополнительно вы можете прочитать перевод записи из блога Джона Резига.

    Прогрессивный асинхронный рендеринг: картина прорисовывается сразу с небольшим количеством деталей, затем деталей появляется всё больше


    При необходимости отрисовать большую структуру с вложенными друг в друга элементами (даже простые вещи чаще всего реализованы, как вложенные части), в том числе при необходимости отрисовать список, фреймворк в первой итерации отрисует вьюхи для моделей, находящихся на самом верху структуры. Внутри этой итерации будет ещё две: будет отрисована самая основа, задающая визуальные габариты, далее будут отрисованы детали (касающиеся непосредственно этой модели, реакции на изменения её состояний). В следующей итерации фреймворк будет рисовать вложенные модели, и так будет продолжаться, пока вложенность не кончится. Все итерации разделены и могут быть разряжены с помощью setTimeout, если отрисовка занимает например более 200 мс (в будущем планирую заменить на requestAnimationFrame), чтобы не блокировать интерфейс и позволить браузеру отрисовать то, что уже есть. Для пользователя интерфейс будет изменяться постепенно, т.е. он будет видеть, что происходят некоторые изменения, в отличие от более простого способа, когда после продолжительной паузы (в течение которой баузер не реагирует на нажатия и отображает одно и тоже) браузер резко меняет картинку.

    Это похоже на прогрессивный jpeg или может напомнить вам игровые движки. В моменты, когда движку не хватает ресурсов, и изображение появляется сначала в плохом качестве (неотфильтрованные текстуры, малополигональные модели и т.д), а потом картинка улучшается.
    Анимация, 2 кадра. Прогрессивный рендеринг: сначала гарабиты, потом детали. Словить момент, и запечатлить первую часть удалось только в режиме отладки, отчего видно затемнение.

    Клонирование нодов



    Получение DOM структуры для View происходит примерно следующим образом: HTML парсер самого браузера один раз разбирает HTML (выполняет самую тяжелую работу, это происходит предварительно, во время загрузки всей страницы), далее, при необходимости, результат клонируется через DOM API (.cloneNode()). Экземляры из шаблона создаются путём клонирования DOM дерева шаблона, после чего читаются и разбираются директивы, связанные с этим нодом.

    Кеширование и использование кеша результатов парсинга DOM шаблонов



    Чтобы после клонирования нодов не приходилось повторно читать атрибуты для поиска директив (такие сервисные данные не копируются при клонировании нода), а так же для повторного использования объектов, которые создаются директивами (для последующего использования шаблонизатором) с целью более эффективного использования памяти и более редкого срабатывания garbadge collector, было найдено решение, которое позволяет максимально быстро копировать сервисные данные из нода-оригинала в нод-клон.

    Происходит это следующим образом: шаблонная DOM структура парсится, анализируется. Всем нодам элементов с директивами устанавливается свойство pvprsd с уникальным значением соответствующее уникальному набору директив, который сохраняется в хранилище и может быть использован в будущем. После того, как всем нужным элементам установлено свойство, начиная от корня, структура раскладывается в массив. Шаблонная структура клонируется от корня, и этот экземпляр так же клонируется. Эксемпляр далее сходным образом от корня раскладывается в массив. Такое клонирование и раскладывание гарантирует, что в массивах под одними индексами будут абсолютно аналогичные ноды. После этого обычный проход по массиву копирует pvprsd из элемента первого массива в элемент массива экземпляра:

    
    var getAll = function(node) {
      var result = [];
      var iteration_list = [ node ];
      var i = 0;
      while( iteration_list.length ){
        var cur_node = iteration_list.shift();
        if ( cur_node.nodeType != 1 ){
          continue;
        }
        for ( i = 0; i < cur_node.childNodes.length; i++ ) {
          iteration_list.push( cur_node.childNodes[i] );
        }
        result.push( cur_node );
      }
      return result;
    };
    

    
    var cloned = this.onode.cloneNode(true);
    var all_onodes = getAll(this.onode);
    var all_cnodes = getAll(cloned);
    


    Заключение



    Я рассказал о способах оптимизаций, реализованных мной в приложении на системном уровне. Это позволяет мне не программировать каждый раз одни и теже оптимизации, а придерживаясь некоторых правил, писать код, получаящийся в результате высопроизводительным. Конечно, я пользуясь и другими способами, использую профайлинг, но это происходит в каких-то конкретных случаях и тратится дополнительное время.

    Мне было бы интересно узнать какие ещё оптимизации могут быть реализованы на системном уровне. Например, автоматическое создание визуально изорилованных областей, на основе элементов iframe/object, — как это сделано вручную в этом примере: http://fb.html5isready.com.


    часть визуального представления приложения находится внутри элемента object



    Такой метод у меня не реализован, но я представляю реалистичным реализовать это в автоматическом режиме — когда шаблон, его экземпляры, стили, оперативные изменения состояний сами попадают в, автоматически созданный, на основе каких-либо критериев, iframe.

    Расскажите, пожалуйста, о других реализациях техник оптимизаций на системном уровне о которых вы знаете (вроде таких или таких). Или о где-то высказанных идеях таких оптимизаций (пример), или о своих идеях, если готовы поделиться.
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 31
    • 0
      А можно ссылку на Ваше расширение?
    • +1
      По поводу кеширования нодов: лично я когда делал микробенчмарков то у меня клонирование элементов было не быстрее чем создание с нуля + патчинг, а на некоторых моментах даже медленнее. Учитывая геморрой который сопровождает клонирование элементов, я так и не понял в чем бонус. Буду рад ссылке на подробное исследование этого момента.
      • 0
        Не понял о каких трудностях связанных с клонированием (учитывая что этот код не нужно каждый раз писать) идёт речь. Я, наверно, плохо описал как именно используется шаблонизация, уделив больше внимания собственно реализации. О каких проблемах идет речь?
        • 0
          Например, value у input/textarea не клонируется
          • 0
            Во первых клонируется.


            О том, что при клонировании нодов свойства объектов (являющихся интерфейсом к dom node) не клонируются я писал. Если нам очень надо клонировать свойство, то это надо делать либо как я описал выше, либо через setAttribute().

            Во вторых зачем клонировать эти свойства из шаблона, если мы их меняем их и пользуемся в экземпляре?
        • 0
          Клонирование корневого нода структуры было медленней, чем result_node.innerHTML = '.....' или медленней чем
          var div = document.createEment('div');
          var pp = document.createEment('p');
          div.appendChild(pp)?

          Т.е. такое поэлементное воссоздание даже для большой структуры
          Реальный код из приложения
          <div class="playlist_panel" pv-nest="plarow">
          	<div class="playlist-actions">
          		<div class="pla-panel">
          			<span class="pla-button" pv-anchor="btrow-multiatcs" pv-events="click::switchPart:row-multiatcs" pv-type="way-point">● ● ●</span>
          			<span class="pl-settings-button" title="Playlists settings" pv-anchor="btrow-pl-settings" pv-events="click::switchPart:row-pl-settings" pv-type="way-point"></span>
          		</div>
          		<div 
          			pv-anchor="row_context"
          			class="pla-row-content has-dark-buttons hidden"
          			pv-class="pla-row-content has-dark-buttons {{!active_part && 'hidden'}}"
          			style="position:relative">
          			<span class="rc-arrow hidden" pv-anchor="arrow" pv-props="style.left: {{arrow_pos}}" pv-class="rc-arrow {{!active_part && 'hidden'}}">
          				<span class=" rc-s-arrow arrow-part"><span class="rc-s-arrow-bg arrow-part"></span></span>
          			</span>
          			<div class="pla-row hidden" pv-nest="context_parts for_model:row-multiatcs" pv-class="pla-row {{!active_view && 'hidden'}}">
          				<div class="left-buttons-group">
          					<span class="button-hole">
          						<a 
          							pv-events="click::makePlayable"
          							pv-type="way-point"
          							class="search-music-files nicebutton lang localize-playlist-getmp3">Find files for compositions</a>
          					</span>
          
          					<!-- href="http://seesu.me/generated_files/seesu_playlist.m3u"-->
          					<span class="button-hole">
          						<a
          							pv-events="click::makeExternalPlaylist"
          							pv-type="way-point"
          							class="open-external-playlist nicebutton lang localize-playlist-export">Save playlist to *.m3u file</a>
          					</span>
          
          				</div>
          				
          			</div>
          			<div class="pla-settings hidden" pv-nest="context_parts for_model:row-pl-settings" pv-class="pla-settings {{!active_view && 'hidden'}}">
          				<label class="dont-rept-pl">
          					<input type="checkbox" pv-anchor="dont-rept-pl"/>
          					<span class="lang localize-dont-rept-pl">Do not repeat playlist</span>
          				</label>
          			</div>
          		</div>
          	</div>
          	<div 
          		class="loader_disallowing_desc hidden"
          		pv-class="loader_disallowing_desc {{!loader_disallowing_desc && 'hidden'}}"
          		pv-text="{{loader_disallowing_desc}}"
          	></div>
          	
          	
          </div>



          будет быстрее чем клонирование каким-нибудь таким кодом?

          var samples = {};
          
          var getSourceNode = function( sample_name ){
          	if (!samples[sample_name]) {
          		samples[sample_name] = $( '.' + sample_name )[0];
          	}
          	return samples[sample_name];
          };
          
          var instance1 = getSourceNode('playlist_panel').cloneNode(true);
          var instance2 = getSourceNode('playlist_panel').cloneNode(true);
          var instance3 = getSourceNode('playlist_panel').cloneNode(true);
          
          • 0
            Чем document.createEment('div');, конечно. Я пробовал использовать cloneNode для «шаблонизации» небольших элементов списка (элементов 10) и в результате код с клонированием + модификации работал не быстрее воссоздания элементов с нуля.
            • 0
              Именно клонирование корневого элемента с помощью cloneNode(true)? где true значит «клонировать, но только сам нод, но и всё дерево»

              Где-то есть на jsperf.com код?

              Я готов написать свою часть которая будет отвечать оговорённым требования шаблонизации по результату и возможностям, вроде
              1) какая-то большая структура
              2) нужно будет получить 100 экземпляров
              3) при передаче новых данных в экземпляр соотствествующие части будут автоматически обновлены

              Можно договориться об этих требованиях и кому интересно — предоставят реализации для тестирования. Ну и соответственно потом замерять скорость получения первого экземляра, скорость получения последующих, скорость применения изменений.
              • 0
                я не видел шаблонизаторов, основанных на клонировании нодов, поэтому каких-то чужих тестов не видел, а свои тесты не делал
          • –1
            мне кажется, или это ну уж очень похоже на ангуляр..?
            в чём плюсы относительно ангуляра?
            • 0
              Похожа только часть отвечающая за представление.

              Для меня плюсы заключаются в том, что у меня реальное разделение на MVC в отличии от angularjs, и нет проблем с производительностью при работе со списками.

              Angularjs, построен так, что просто отрендерить большой список и изменить какое-нибудь поле у одного из элементов выливается в геморой с производительностью (он пойдёт по всему списку смотреть не изменилось ли чего у кого, заново выполняя функции на моделях, которые являются источником информации состояний. $digest… вот это всё)
              • +1
                а можно поподробнее про «реальное разделение на MVC»?
                т.е. где в AngularJS оно нереальное, и как оно должно быть на самом деле..?
                По поводу проблем с производительностью — не совсем правда, т.е. проблемы возникают при ну оочень больших списках, и есть способы с этим бороться. Вот, к примеру, реализация таблички — gdepourtales.github.io/ng-cells/performance.html#?rows=5000&cols=1000

                Для личного развития — я всецело «за». Но преимуществ перед более-мнее сложившимися фреймворками не вижу.
                • 0
                  В том примере скроллинг не нативный; шаг скроллинга не пиксел, а целая строка, что точно не плюс. «не совсем правда», «есть способы с этим бороться» — не хотелось бы заниматься борьбой с фреймворком и постоянно решать одну и туже проблему (даже если решение известно, хотя то, что по ссылке точно не решение)

                  Чтобы понять как должно быть организовано реально разделение на MVC нужно представить как использовать angularjs в системе, где ядро приложения (с моделями) находится в отдельной области видимости от представления (где вьюхи) и они могут общаться между собой чем-нибудь вроде window.postMessage, вьюха может быть полностью уничтожена, и иногда её нужно полностью воссоздать заново. Представить как использовать angularjs в системах, которые построенный на этом принципе, таких как www.appcelerator.com/titanium/. Нужно понять как во view будет передаваться структура/взаимосвязи моделей, их состояний, как будут передаваться оперативные обновления состояний, структуры во вьюхи, и как будет поставляться обратная связь из вьюх в модели.

                  Система шаблонизации и датабайндинг в angularjs — единственные вещи, которые интересны в нём.

                  Пока никому не предлагаю использовать свой «велосипед». Действительно трудно найти преимущества, когда я о них не рассказываю :)

                  С другой стороны непонято — о каких именно «более-менее сложившихся фреймворках» идёт речь в контексте рендеринга? О каких фрейморках с выдающейся системой рендеринга вы знаете? Я знаю только шаблонизацию angularjs и шаблонизацию facebook react (это не mvc фрейморк), но facebook react опубликован 25 мая 2013 github.com/facebook/react/tree/75897c2dcd1dd3a6ca46284dd37e13d22b4b16b4, а я начал внедрять декларативную шаблонизацию в стиле angularjs примерно в феврале 2013 (при этом у меня уже было до этого атомарное изменении DOM) и к концу апреля 2013 у меня была работающее в декларативном стиле решение, покрывающее большинство вещей, требующихся мне. И с тех пор я в той части занимался в основном оптимизациями. Какой выбор был тогда у меня и какой сейчас?
                  • 0
                    Так я и не понял из Вашего объяснения, чем AngularJS — не MVC, и что там сделано не так, как должно быть.
                    жаль.
                    • +1
                      Если вам нравится дата-байндинги, посмотрите на библиотеку KnockoutJS. мне очень нравится то, что она предоставляет именно шаблонизатор и ничего больше и в этом случае её можно удобно интегрировать в свой фреймворк.
                  • 0
                    он пойдёт по всему списку смотреть не изменилось ли чего у кого, заново выполняя функции на моделях, которые являются источником информации состояний. $digest… вот это всё
                    Можно сделать директиву которая будет «точечно» обновлять по команде без проверок изменений, просто обычно этого не требуется.

                    Система шаблонизации и датабайндинг в angularjs — единственные вещи, которые интересны в нём.
                    Ну, ещё и директивы.
                • +1
                  Посмотрите в сторону shadow dom и вообще веб-компонентов, полимер тут в самый раз.
                  Вы-то это можете, у вас все под вебкит тут заточено последний.
                  Если рассказывать кратко — та же изоляция на уровне дом-дерева, но при этом гораздо более удобный интерфейс.
                  • 0
                    Почему же под вебкит? Тут, кажется, все оптимизации касаются всех браузеров. К примеру приложение достаточно хорошо себя чувствует в устаревающей опере 12
                    • 0
                      хм, по сайту приложения почему-то подумал, что это под оперу 15+, и сделано было потому что просто легко было перетащить под нее)
                  • 0
                    Есть полезные статьи от Google и Opera на эту тему:
                    Optimizing JavaScript
                    Minimizing reflows
                    Efficient JavaScript
                    Там также есть примеры оптимизации кода.
                  • 0
                    о других реализациях техник оптимизаций на системном уровне о которых вы знаете

                    В местах где jQuery вызывается часто, можно заменить jQuery на что-нибудь полегче, либо на «нативные» вызовы, т.к. иногда создание «jQuery объекта» дороже вызова целевого метода. Пример.
                    Таким методом я ускорил одно толстое приложение в 2 раза.
                    • 0
                      Метод хороший, но не уверен, что тест показательный. В каком реальном проекте вам нужно выбирать теги 500000 подряд? Смысл этого, ведь достаточно, как правило 1 раза :). Да и странновато — подключать angular light с fquery для вызова нативных методов. А в целом да, писать нативно с полифилами лучше, чем писать jQuery.
                      • 0
                        В каком реальном проекте вам нужно выбирать теги 500000 подряд?
                        Но jQuery используется не только для выборок, вот пример выборка + замена текста, 5 тыс итераций. Т.е. сэкономить все же можно.

                        Да и странновато — подключать angular light с fquery для вызова нативных методов.
                        Согласен, просто тут он оказался под рукой (для вывода результатов).
                    • 0
                      Вместо «эвАлюционировал» надо «эвОлюционировал». Проверочное слово: «Mitsubishi Lancer EvOlution».
                      • +3
                        Статья очень крутая! Такого количества оптимизаций и такого сложного продукта ждешь скорее от какого-нибудь софтверного гиганта, чем от разработчика, ковыряющего это в свободное время. Во всем ангуляре поди оптимизаций меньше :) Легко представить аналогичного уровня статью, например, «как мы делали чтобы Яндекс.Музыка не тормозила в браузере». Хотя по ощущениям она и попроще, и по-тормознее будет.
                        • 0
                          Анимация, 2 кадра. Прогрессивный рендеринг: сначала гарабиты, потом детали. Словить момент, и запечатлить первую часть удалось только в режиме отладки, отчего видно затемнение.

                          Если так быстро рисуется, стоит ли заморачиваться с прогрессивным рендерингом?
                          • 0
                            иногда список может быть большой, иногда нужно полностью восстановить большую DOM структуру

                            Например, тут chrome.google.com/webstore/detail/seesu-music/nhonlochieibnkmfpombklkgjpkeckhi когда popup закрывается его document удаляется, и при новом открытии приходится работать с новым документом
                            • 0
                              кроме того оптимизация реализована, и мне не нужно об этом думать :)

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.