В чем разница между функцией компиляции и компоновки в angularjs

217

Может кто-нибудь объяснить в простых терминах?

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

Теги:

8 ответов

215
  • функция компиляции - используйте для шаблона DOM-манипуляцию (т.е. манипулирование элементом tElement = template), следовательно, манипуляции, которые применяются ко всем клонам DOM шаблона, связанным с директивой.

  • Функция ссылок - использование для регистрации DOM-прослушивателей (т.е. $watch выражений в области экземпляра), а также экземпляр DOM-манипуляции (т.е. манипулирование iElement = отдельным элементом экземпляра).
    Выполняется после клонирования шаблона. Например, внутри < li ng-repeat... > , функция ссылки выполняется после того, как шаблон <li> (tElement) был клонирован (в iElement) для этого конкретного элемента <li> . A $watch() позволяет директиве быть уведомленным об изменениях свойств экземпляра экземпляра (область экземпляра связана с каждым экземпляром), что позволяет директиве визуализировать обновленное значение экземпляра для DOM - путем копирования содержимого из области экземпляра в DOM.

Обратите внимание, что преобразования DOM могут выполняться в функции компиляции и/или в функции ссылок.

В большинстве директив требуется только функция ссылок, так как большинство директив имеют дело только с конкретным экземпляром элемента DOM (и его областью экземпляра).

Один из способов помочь определить, что использовать: считайте, что функция компиляции не получает аргумент scope. (Я намеренно игнорирую аргумент функции связывания трансключи, который получает transcluded scope - это редко используется). Таким образом, функция компиляции не может делать ничего, что вы для этого требуется область (экземпляр) - вы не можете смотреть какие-либо свойства области экземпляра модели/экземпляра, вы не можете манипулировать DOM с использованием информации о возможности экземпляра экземпляра, вы не можете вызывать функции, определенные в области экземпляра, и т.д..

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

Здесь пример директивы, которая также использует только функцию компиляции. Директиве нужно только преобразовать шаблон DOM, поэтому можно использовать функцию компиляции.

Другой способ помочь определить, что использовать: если вы не используете параметр "element" в функции ссылок, вам, вероятно, не нужна функция ссылки.

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

Обратите внимание, что если вам нужна функция компиляции и функция ссылки (или функции предварительной и последующей ссылки), функция компиляции должна вернуть функцию (-ы) ссылки, потому что атрибут 'link' игнорируется, если атрибут 'compile' определены.

См. также

  • 5
    Лучшее объяснение по компиляции против ссылки.
  • 1
    Когда вы говорите, if you don't use the "element" parameter in the link function, then you probably don't need a link function. Вы имеете в виду «сфера» вместо «элемент»?
70

Я бил головой о стену в течение нескольких дней, и я чувствую, что немного больше объяснений в порядке.

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

В наших целях я собираюсь подчеркнуть терминологию, которая в противном случае путается:

Компилятор SERVICE ($ compile) - это механизм angular, который обрабатывает DOM и запускает различные биты кода в директивах.

Компиляция FUNCTION - это один бит кода внутри директивы, который запускается в определенное время BY SERVICE ($ compile).

Некоторые примечания о компиляции FUNCTION:

  • Вы не можете изменить элемент ROOT (тот, на который действует ваша директива), поскольку он уже скомпилирован с внешнего уровня DOM (компиляция SERVICE уже проверена на директивы для этого элемента).

  • Если вы хотите добавить другие директивы в (вложенные) элементы, вы либо:

    • Необходимо добавить их на этапе компиляции.

    • Имейте инъекцию службы компиляции в фазу связывания и скомпилируйте элементы вручную. НО, остерегайтесь компиляции чего-то дважды!

Также полезно посмотреть, как работают вложенные и явные вызовы компиляции $, поэтому я создал игровую площадку для просмотра в http://jsbin.com/imUPAMoV/1/edit. В основном, он просто записывает шаги в console.log.

Я расскажу о результатах того, что вы увидите в этом бункере. Для DOM пользовательских директив tp и sp вложен следующим образом:

<tp>
   <sp>
   </sp>
</tp>

Angular Скомпилировать SERVICE вызывается:

tp compile
sp compile
tp pre-link
sp pre-link
sp post-link
tp post-link

Код jsbin также имеет функцию tp post-link FUNCTION, которая явно вызывает компиляцию SERVICE в третьей директиве (вверх), которая выполняет все три этапа в конце.

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

СЦЕНАРИЙ 1: Директива как МАКРО

Вы хотите динамически добавлять директиву (скажем ng-show) к чему-то в своем шаблоне, который вы можете получить из атрибута.

Скажем, у вас есть templateUrl, который указывает на:

<div><span><input type="text"></span><div>

и вы хотите создать настраиваемую директиву:

<my-field model="state" name="address"></my-field>

который превращает DOM в это:

<div><span ng-show="state.visible.address"><input ng-model="state.fields.address" ...>

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

Это очень полезно для фазы компиляции, поскольку вы можете основывать все манипуляции DOM на вещах, которые вы знаете только из атрибутов. Просто используйте jQuery для добавления атрибутов:

compile: function(tele, tattr) {
   var span = jQuery(tele).find('span').first();
   span.attr('ng-show', tattr.model + ".visible." + tattr.name);
   ...
   return { 
     pre: function() { },
     post: function() {}
   };
}

Последовательность операций будет (вы можете увидеть это через jsbin, упомянутый ранее):

  • Скомпилированный сервис SERVICE находит мое поле
  • Он вызывает компиляцию FUNCTION в директиве, которая обновляет DOM.
  • Затем компилят SERVICE переходит в итоговую DOM и формирует (рекурсивно)
  • Компиляция SERVICE затем вызывает предварительную ссылку сверху вниз
  • Затем компиляция SERVICE вызывает постсвязь BOTTOM UP, поэтому функция ссылки my-field называется AFTER, внутренние узлы связаны.

В приведенном выше примере привязка не требуется, так как вся директивная работа выполнялась при компиляции FUNCTION.

В любой момент код в директиве может запросить работу SERVICE для дополнительных элементов.

Это означает, что мы можем сделать то же самое в функции ссылок, если вы введете службу компиляции:

directive('d', function($compile) {
  return {
    // REMEMBER, link is called AFTER nested elements have been compiled and linked!
    link: function(scope, iele, iattr) {
      var span = jQuery(iele).find('span').first();
      span.attr('ng-show', iattr.model + ".visible." + iattr.name);
      // CAREFUL! If span had directives on it before
      // you will cause them to be processed again:
      $compile(span)(scope);
    }
});

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

Таким образом, фаза компиляции является гораздо лучшим выбором для работы в макро-стиле.

СЦЕНАРИЙ 2: конфигурация DOM по данным области

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

Итак, скажем, вы хотите сутенерствовать ввод с проверками, но вы хотите экспортировать свои проверки из ORM-сервера на стороне сервера (DRY) и автоматически их применять и генерировать надлежащий клиентский интерфейс для эти проверки.

Ваша модель может нажать:

scope.metadata = {
  validations: {
     address: [ {
       pattern: '^[0-9]',
       message: "Address must begin with a number"
     },
     { maxlength: 100,
       message: "Address too long"
     } ]
  }
};
scope.state = {
  address: '123 Fern Dr'
};

и вам может понадобиться директива:

<form name="theForm">
  <my-field model="state" metadata="metadata" name="address">
</form>

для автоматического включения соответствующих директив и разделов для отображения различных ошибок проверки:

<form name="theForm">
  <div>
    <input ng-model="state.address" type="text">
    <div ng-show="theForm.address.$error.pattern">Address must begin with a number</input>
...

В этом случае вам определенно нужен доступ к области (так как там хранятся ваши проверки), и вам придется скомпилировать дополнения вручную, снова стараясь не дублировать вещи. (в качестве дополнительной заметки вам нужно будет установить имя в теге содержащейся формы (я принимаю здесь форму) и могу получить к нему доступ по ссылке с iElement.parent(). controller ('form'). $name).

В этом случае нет смысла писать функцию компиляции. Ссылка действительно то, что вы хотите. Этапы:

  • Определите шаблон, полностью лишенный директив angular.
  • Определить функцию связи, которая добавляет различные атрибуты
  • УДАЛИТЕ любые директивы angular, которые вы можете разрешить для своего элемента верхнего уровня (директива my-field). Они уже обработаны, и это способ предотвратить двойную обработку.
  • Завершите, вызвав службу компиляции SERVICE на свой элемент верхнего уровня.

Так же:

angular.module('app', []).
directive('my-field', function($compile) {
  return {
    link: function(scope, iele, iattr) {
      // jquery additions via attr()
      // remove ng attr from top-level iele (to avoid duplicate processing)
      $compile(iele)(scope); // will pick up additions
    }
  };
});

Конечно, вы могли бы скомпилировать вложенные элементы один за другим, чтобы не беспокоиться о дублировании обработки директив ng при компиляции элемента верхнего уровня.

Последнее замечание по этому сценарию: я подразумеваю, что вы будете настаивать на определении валидации с сервера, и в моем примере я показал их как данные уже в области. Я оставляю это как упражнение для читателя, чтобы выяснить, как можно справиться с необходимостью извлекать эти данные из REST API (подсказка: отложенная компиляция).

СЦЕНАРИЙ 3: привязка двухсторонних данных по ссылке

Конечно, наиболее распространенное использование ссылки - просто подключить двустороннюю привязку данных через watch/apply. Большинство директив попадают в эту категорию, поэтому они адекватно охвачены в других местах.

  • 2
    +1 Мне нравится твой ответ. это более информативно.
  • 2
    Awsome & Cool Ответ!
Показать ещё 1 комментарий
50

В основе Angular framework лежит синтаксический анализатор. Парсер, который анализирует директивы Angular и выводит вывод HTML.

Angular парсер работает в 3 этапа: -

  • Шаг 1: - HTML-браузер анализирует HTML-код и создает DOM (документ Объектная модель).

  • Шаг 2: структура Angular работает над этим DOM, смотря на Angular директивы и соответственно манипулируют DOM.

  • Шаг 3: - Эта манипуляция затем отображается как HTML в браузере.

Изображение 3683

Теперь приведенный выше анализ Angular не так прост, как кажется. Он встречается в двух фазах "Компиляция" и "Ссылка" . Сначала выполняется фаза компиляции, а затем фаза связи.

Изображение 3684

В фазе компиляции парсер Angular начинает синтаксический анализ DOM и всякий раз, когда парсер сталкивается с директивой, он создает функцию. Эти функции называются шаблонами или скомпилированными функциями. На этом этапе у нас нет доступа к данным $scope.

В фазе ссылки данные, то есть ($ scope), прикрепляются к функции шаблона и выполняются для получения окончательного вывода HTML.

Изображение 3685

50

Из документов:

Compiler

Компилятор - это служба angular, которая пересекает DOM, ища атрибуты. Процесс компиляции происходит в две фазы.

  • Скомпилировать: пересечь DOM и собрать все директивы. Результатом является функция связывания.

  • Ссылка: объединить директивы с областью действия и создать живой просмотр. Любые изменения в модели области отражены в представлении, и любые пользовательские взаимодействия с представлением отражаются в модели области. Создание модели области - единственный источник истины.

Некоторые директивы, такие как ng-repeat, клонируют элементы DOM один раз для каждого элемента в коллекции. Наличие фазы компиляции и ссылки улучшает производительность, поскольку клонированный шаблон нужно только один раз компилировать, а затем связывать один раз для каждого экземпляра клона.

Итак, по крайней мере, в некоторых случаях две фазы существуют отдельно как оптимизация.


От @UmurKontacı:

Если вы собираетесь делать преобразования DOM, это должно быть compile. Если вы хотите добавить некоторые функции, которые являются изменениями поведения, они должны быть в link.

  • 46
    Если вы собираетесь делать DOM преобразование, его следует compile если вы хотите добавить некоторые функции, изменяющие поведение, это должно быть в link .
  • 4
    +1 к вышеуказанному комментарию; это самое краткое описание, которое я нашел до сих пор. Это соответствует учебнику, который я нашел здесь .
17

Это от Misko говорить о директивах. http://youtu.be/WqmeI5fZcho?t=16m23s

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

  • 0
    Я думаю, что это видео ясно объясняет разницу.
9

Немного поздно в тему. Но для будущих читателей:

Я наткнулся на следующее видео, которое объясняет Compile и Link в Angular JS очень просто:

https://www.youtube.com/watch?v=bjFqSyddCeA

Было бы неудобно копировать/вводить все содержимое здесь. Я сделал несколько снимков экрана из видео, которые объясняют каждый этап фаз компиляции и связи:

Изображение 3686

Изображение 3687

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

Первый цикл: "Компиляция" выполняется сначала во всех директивах.
Второй цикл: "Контроллер" и "Предварительная ссылка" выполняются (только один за другим) Третий цикл: "Пост-ссылка" выполняется в обратном порядке (начиная с самого внутреннего).

Ниже приведен код, демонстрирующий выше:

var app = angular.module('app', []);

app.controller('msg', ['$scope', function($scope){

}]);

app.directive('message', function($interpolate){
    return{

        compile: function(tElement, tAttributes){ 
            console.log(tAttributes.text + " -In compile..");
            return {

                pre: function(scope, iElement, iAttributes, controller){
                    console.log(iAttributes.text + " -In pre..");
                },

                post: function(scope, iElement, iAttributes, controller){
                    console.log(iAttributes.text + " -In Post..");
                }

            }
        },

        controller: function($scope, $element, $attrs){
            console.log($attrs.text + " -In controller..");
        },

    }
});
<body ng-app="app">
<div ng-controller="msg">
    <div message text="first">
        <div message text="..second">
            <div message text="....third">

            </div>              
        </div>  
    </div>
</div>

UPDATE:

Часть 2 этого же видео доступна здесь: https://www.youtube.com/watch?v=1M3LZ1cu7rw Видео объясняет больше о том, как изменить DOM и обработать события во время компиляции и Процесс связывания Angular JS, в простом примере.

  • 0
    Использовал compile и post для изменения DOM до того, как он будет изменен в части template из директивы vendor.
6

Две фазы: компиляция и ссылка

Скомпилировать:

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

Ссылка:

Когда элемент связан, дерево DOM разбивается в своей точке ветвления дерева DOM, а содержимое заменяется скомпилированным (и связанным) экземпляром шаблона. Исходное перемещенное содержимое либо отбрасывается, либо, в случае перехода, повторно связано с шаблоном. С переходом две части связаны друг с другом (вроде как цепочка, причем часть шаблона находится посередине). Когда вызывается функция ссылки, шаблон уже привязан к области видимости и добавляется как дочерний элемент этого элемента. Функция ссылки - это возможность манипулировать DOM дальше и настраивать слушателей изменений.

3

Этот вопрос старый, я хотел бы сделать краткое резюме, которое может помочь:

  • Скомпилировать вызов один раз для всех экземпляров директивы
  • Скомпилировать главную цель - вернуть/создать ссылку (и, возможно, pre/post) функцию/объект. Вы также можете инициализировать материал, который делится между экземплярами директивы.
  • По-моему, "ссылка" является запутанным именем для этой функции. Я предпочел бы "предварительную визуализацию". Ссылка
  • вызывается для каждого экземпляра директивы, и ее целью является подготовка рендеринга директивы в DOM.
  • 1
    один плюс за название предложения: «pre-render»

Ещё вопросы

Сообщество Overcoder
Наверх
Меню