Was ist der Unterschied zwischen Kompilierungs- und Verknüpfungsfunktion in Angularjs?


208

Kann jemand in einfachen Worten erklären?

Die Dokumentation wirkt etwas stumpf. Ich verstehe nicht die Essenz und das Gesamtbild, wann ich sie übereinander anwenden soll. Ein Beispiel, das die beiden gegenüberstellt, wäre fantastisch.


2
Vielleicht ein umfassenderer Überblick über Direktivenfunktionen: Angular-Direktiven - wann Kompilierung, Controller, Pre-Link und Post-Link verwendet werden sollen .
Izhaki

Antworten:


217
  • Kompilierungsfunktion - Verwendung für die DOM-Manipulation von Vorlagen (dh Manipulation von tElement = Template-Element), daher Manipulationen, die für alle DOM-Klone der Vorlage gelten, die der Direktive zugeordnet sind.

  • Link - Funktion - Einsatz für DOM Zuhörer Registrierung (dh $ Uhr Ausdrücke auf dem Instanzbereich) sowie Instanz DOM - Manipulation (dh Manipulation von IElement = einzelnem Instanz - Elemente).
    Es wird ausgeführt, nachdem die Vorlage geklont wurde. Beispielsweise wird innerhalb einer <li ng-repeat ...> die Verknüpfungsfunktion ausgeführt, nachdem die <li> -Vorlage (tElement) für dieses bestimmte <li> -Element (in ein iElement) geklont wurde.
    Mit $ watch () kann eine Direktive über Änderungen der Eigenschaften des Instanzbereichs benachrichtigt werden (jeder Instanz ist ein Instanzbereich zugeordnet). Dadurch kann die Direktive einen aktualisierten Instanzwert in das DOM rendern, indem Inhalte aus dem Instanzbereich in kopiert werden das DOM.

Beachten Sie, dass DOM-Transformationen in der Kompilierungsfunktion und / oder der Verknüpfungsfunktion durchgeführt werden können.

Die meisten Direktiven benötigen nur eine Verknüpfungsfunktion, da die meisten Direktiven nur eine bestimmte DOM-Elementinstanz (und ihren Instanzbereich) behandeln.

Eine Möglichkeit, um zu bestimmen, welche verwendet werden soll: Beachten Sie, dass die Kompilierungsfunktion kein scopeArgument erhält . (Ich ignoriere absichtlich das Argument der Transclude-Verknüpfungsfunktion, das einen transcluded-Bereich erhält - dies wird selten verwendet.) Die Kompilierungsfunktion kann also nichts tun, was Sie tun möchten, was einen (Instanz-) Bereich erfordert - Sie können Sie können keine Modell- / Instanzbereichseigenschaften beobachten, Sie können das DOM nicht mithilfe von Instanzbereichsinformationen bearbeiten, Sie können keine Funktionen aufrufen, die im Instanzbereich definiert sind usw.

Die Kompilierungsfunktion (wie die Verknüpfungsfunktion) hat jedoch Zugriff auf die Attribute. Wenn für Ihre DOM-Manipulationen der Instanzbereich nicht erforderlich ist, können Sie eine Kompilierungsfunktion verwenden. Hier ist ein Beispiel für eine Direktive, die aus diesen Gründen nur eine Kompilierungsfunktion verwendet. Es untersucht die Attribute, benötigt jedoch keinen Instanzbereich, um seine Arbeit zu erledigen.

Hier ist ein Beispiel für eine Direktive, die auch nur eine Kompilierungsfunktion verwendet. Die Direktive muss nur das Template-DOM transformieren, damit eine Kompilierungsfunktion verwendet werden kann.

Eine andere Möglichkeit, um zu bestimmen, welche verwendet werden soll: Wenn Sie den Parameter "element" in der Verknüpfungsfunktion nicht verwenden, benötigen Sie wahrscheinlich keine Verknüpfungsfunktion.

Da die meisten Direktiven eine Linkfunktion haben, werde ich keine Beispiele nennen - sie sollten sehr leicht zu finden sein.

Beachten Sie, dass, wenn Sie eine Kompilierungsfunktion und eine Verknüpfungsfunktion (oder Funktionen vor und nach der Verknüpfung) benötigen, die Kompilierungsfunktion die Verknüpfungsfunktion (en) zurückgeben muss, da das Attribut 'link' ignoriert wird, wenn das Attribut 'compile' definiert ist.

Siehe auch


5
Beste Erklärung zum Kompilieren gegen Link.
Nexus23

1
Wenn Sie sagen if you don't use the "element" parameter in the link function, then you probably don't need a link function., meinen Sie "Umfang" anstelle von "Element"?
Jason Larke

69

Ich habe ein paar Tage lang meinen Kopf gegen die Wand geschlagen, und ich denke, dass ein bisschen mehr Erklärung angebracht ist.

Grundsätzlich wird in den Dokumenten erwähnt, dass die Trennung weitgehend eine Leistungssteigerung darstellt. Ich möchte wiederholen, dass die Kompilierungsphase hauptsächlich verwendet wird, wenn Sie das DOM ändern müssen, bevor die Unterelemente selbst kompiliert werden.

Für unsere Zwecke werde ich die Terminologie hervorheben, die ansonsten verwirrend ist:

Der Compiler SERVICE ($ compile) ist der Winkelmechanismus, der das DOM verarbeitet und die verschiedenen Codebits in Direktiven ausführt.

Die Kompilierungsfunktion ist ein Codebit innerhalb einer Direktive, die zu einem bestimmten Zeitpunkt vom Compiler SERVICE ($ compile) ausgeführt wird.

Einige Hinweise zur Kompilierung FUNCTION:

  1. Sie können das ROOT-Element (das Element, auf das sich Ihre Direktive auswirkt) nicht ändern, da es bereits von der äußeren Ebene von DOM kompiliert wird (der Kompilierungs-SERVICE hat bereits nach Direktiven für dieses Element gesucht).

  2. Wenn Sie (verschachtelten) Elementen weitere Anweisungen hinzufügen möchten, haben Sie entweder:

    1. Müssen sie während der Kompilierungsphase hinzufügen.

    2. Sie müssen den Kompilierungsdienst in die Verknüpfungsphase einfügen und die Elemente manuell kompilieren. ABER, hüte dich davor, etwas zweimal zu kompilieren!

Es ist auch hilfreich zu sehen, wie die Verschachtelung und die expliziten Aufrufe von $ compile funktionieren. Daher habe ich unter http://jsbin.com/imUPAMoV/1/edit einen Spielplatz zum Anzeigen erstellt . Grundsätzlich werden nur die Schritte in console.log protokolliert.

Ich werde die Ergebnisse dessen, was Sie in diesem Papierkorb sehen würden, hier angeben. Für ein DOM mit benutzerdefinierten Direktiven sind tp und sp wie folgt verschachtelt:

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

Angular Compile SERVICE ruft auf:

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

Der jsbin-Code hat auch die Funktion tp post-link FUNCTION, die den Kompilierungs-SERVICE explizit für eine dritte Direktive (up) aufruft, die am Ende alle drei Schritte ausführt.

Jetzt möchte ich einige Szenarien durchgehen, um zu zeigen, wie man die Kompilierung und den Link verwenden kann, um verschiedene Dinge zu tun:

SZENARIO 1: Richtlinie als MAKRO

Sie möchten eine Anweisung (z. B. ng-show) dynamisch zu etwas in Ihrer Vorlage hinzufügen, das Sie aus einem Attribut ableiten können.

Angenommen, Sie haben eine templateUrl, die auf Folgendes verweist:

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

und Sie möchten eine benutzerdefinierte Direktive:

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

das macht das DOM zu folgendem:

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

Grundsätzlich möchten Sie die Boilerplate reduzieren, indem Sie eine konsistente Modellstruktur haben, die Ihre Direktive interpretieren kann. Mit anderen Worten: Sie möchten ein Makro.

Dies ist eine großartige Verwendung für die Kompilierungsphase, da Sie alle DOM-Manipulationen auf Dingen basieren können, die Sie nur aus den Attributen kennen. Verwenden Sie einfach jQuery, um die Attribute hinzuzufügen:

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() {}
   };
}

Die Reihenfolge der Operationen ist (Sie können dies über den zuvor erwähnten jsbin sehen):

  1. Der Kompilierungs-SERVICE findet mein Feld
  2. Es ruft die Kompilierungsfunktion FUNCTION für die Direktive auf, die das DOM aktualisiert.
  3. Der Kompilierungsdienst betritt dann das resultierende DOM und KOMPILIERT (rekursiv)
  4. Der Kompilierungsdienst ruft dann den Pre-Link von oben nach unten auf
  5. Der Kompilierungs-SERVICE ruft dann BOTTOM UP nach der Verknüpfung auf, sodass die Verknüpfungsfunktion von my-field aufgerufen wird, nachdem interne Knoten verknüpft wurden.

Im obigen Beispiel ist keine Verknüpfung erforderlich, da die gesamte Arbeit der Direktive in compile FUNCTION ausgeführt wurde.

Der Code in einer Direktive kann jederzeit verlangen, dass der Compiler SERVICE auf zusätzlichen Elementen ausgeführt wird.

Dies bedeutet, dass wir in einer Link-Funktion genau dasselbe tun können, wenn Sie den Kompilierungsdienst einfügen:

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);
    }
});

Wenn Sie sicher sind, dass die Elemente, die Sie an $ compile SERVICE übergeben, ursprünglich direktivenfrei waren (z. B. aus einer von Ihnen definierten Vorlage stammen oder Sie sie nur mit angle.element () erstellt haben), ist das Endergebnis ziemlich genau das gleiche wie zuvor (obwohl Sie möglicherweise einige Arbeiten wiederholen). Wenn das Element jedoch andere Anweisungen enthält, haben Sie diese nur erneut verarbeitet, was zu allen möglichen fehlerhaften Verhaltensweisen führen kann (z. B. doppelte Registrierung von Ereignissen und Uhren).

Daher ist die Kompilierungsphase eine viel bessere Wahl für Arbeiten im Makrostil.

SZENARIO 2: DOM-Konfiguration über Bereichsdaten

Dieser folgt aus dem obigen Beispiel. Angenommen, Sie benötigen Zugriff auf den Bereich, während Sie das DOM bearbeiten. In diesem Fall ist der Kompilierungsabschnitt für Sie nutzlos, da dies geschieht, bevor ein Bereich verfügbar ist.

Angenommen, Sie möchten eine Eingabe mit Validierungen aufpeppen, aber Ihre Validierungen aus einer serverseitigen ORM-Klasse (DRY) exportieren und sie automatisch anwenden lassen und die richtige clientseitige Benutzeroberfläche für diese Validierungen generieren.

Ihr Modell könnte pushen:

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'
};

und Sie möchten vielleicht eine Richtlinie:

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

So fügen Sie die richtigen Anweisungen und Divs automatisch ein, um die verschiedenen Validierungsfehler anzuzeigen:

<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>
...

In diesem Fall benötigen Sie auf jeden Fall Zugriff auf den Bereich (da dort Ihre Validierungen gespeichert sind) und müssen die Ergänzungen manuell kompilieren, wobei Sie erneut darauf achten müssen, die Dinge nicht doppelt zu kompilieren. (Als Randnotiz müssten Sie einen Namen für das enthaltende Formular-Tag festlegen (ich nehme hier die Form an) und könnten in Verbindung mit iElement.parent () darauf zugreifen. controller ('form'). $ name) .

In diesem Fall macht es keinen Sinn, eine Kompilierungsfunktion zu schreiben. Link ist wirklich das, was Sie wollen. Die Schritte wären:

  1. Definieren Sie eine Vorlage, die keine Winkelanweisungen enthält.
  2. Definieren Sie eine Verknüpfungsfunktion, die die verschiedenen Attribute hinzufügt
  3. ENTFERNEN Sie alle Winkelanweisungen, die Sie möglicherweise für Ihr Element der obersten Ebene (die my-field-Anweisung) zulassen. Sie wurden bereits verarbeitet, und dies ist eine Möglichkeit, um zu verhindern, dass sie doppelt verarbeitet werden.
  4. Rufen Sie zum Abschluss den Kompilierungs-SERVICE für Ihr Element der obersten Ebene auf

Wie so:

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
    }
  };
});

Sie können die verschachtelten Elemente natürlich einzeln kompilieren, um zu vermeiden, dass Sie sich beim erneuten Kompilieren des Elements der obersten Ebene Gedanken über die doppelte Verarbeitung von ng-Anweisungen machen müssen.

Ein letzter Hinweis zu diesem Szenario: Ich habe impliziert, dass Sie die Definition der Validierungen von einem Server aus verschieben würden, und in meinem Beispiel habe ich sie als Daten angezeigt, die bereits im Bereich enthalten sind. Ich überlasse es dem Leser als Übung, herauszufinden, wie man damit umgehen kann, dass diese Daten aus einer REST-API abgerufen werden müssen (Hinweis: verzögerte Kompilierung).

SZENARIO 3: Zweiwege-Datenbindung über Link

Natürlich besteht die häufigste Verwendung von Links darin, die bidirektionale Datenbindung einfach über watch / apply anzuschließen. Die meisten Richtlinien fallen in diese Kategorie, sodass sie an anderer Stelle angemessen behandelt werden.


2
Ehrfürchtige & coole Antwort!
Nexus23

Wie füge ich verschachtelte Elemente hinzu, ohne sie doppelt zu kompilieren?
Art713

50

Aus den Dokumenten:

Compiler

Der Compiler ist ein Winkeldienst, der das DOM nach Attributen durchsucht. Der Kompilierungsprozess erfolgt in zwei Phasen.

  1. Kompilieren: Durchlaufen Sie das DOM und sammeln Sie alle Anweisungen. Das Ergebnis ist eine Verknüpfungsfunktion.

  2. Link: Kombinieren Sie die Anweisungen mit einem Gültigkeitsbereich und erstellen Sie eine Live-Ansicht. Alle Änderungen im Bereichsmodell werden in der Ansicht angezeigt, und alle Benutzerinteraktionen mit der Ansicht werden im Bereichsmodell angezeigt. Das Scope-Modell zu einer einzigen Quelle der Wahrheit machen.

Einige Anweisungen ng-repeatklonen solche DOM-Elemente einmal für jedes Element in der Sammlung. Eine Kompilierungs- und Verknüpfungsphase verbessert die Leistung, da die geklonte Vorlage nur einmal kompiliert und dann für jede Kloninstanz einmal verknüpft werden muss.

Zumindest in einigen Fällen existieren die beiden Phasen als Optimierung getrennt.


Von @ UmurKontacı :

Wenn Sie DOM-Transformationen durchführen möchten, sollte dies der Fall sein compile. Wenn Sie einige Funktionen hinzufügen möchten, bei denen es sich um Verhaltensänderungen handelt, sollte dies der Fall sein link.


46
Wenn Sie eine DOMTransformation durchführen compilemöchten , sollte dies der Fall sein, wenn Sie einige Funktionen hinzufügen möchten, bei denen es sich um Verhaltensänderungen handelt link.
Umur Kontacı

4
+1 bis obigen Kommentar; Dies ist die prägnanteste Beschreibung, die ich bisher gefunden habe. Es stimmt mit dem Tutorial überein, das ich hier gefunden habe .
Benny Bottema

18

Dies geht aus Miskos Vortrag über Richtlinien hervor. http://youtu.be/WqmeI5fZcho?t=16m23s

Stellen Sie sich die Compilerfunktion als das vor, was an einer Vorlage funktioniert, und als das, was die Vorlage selbst ändern darf, indem Sie beispielsweise eine Klasse hinzufügen oder ähnliches. Es ist jedoch die Verknüpfungsfunktion, die die beiden Aufgaben tatsächlich miteinander verbindet, da die Verknüpfungsfunktion Zugriff auf den Bereich hat und die Verknüpfungsfunktion für jede Instanziierung der jeweiligen Vorlage einmal ausgeführt wird. Die einzigen Dinge, die Sie in die Kompilierungsfunktionen einfügen können, sind Dinge, die in allen Instanzen gemeinsam sind.


10

Etwas spät zum Thread. Aber zum Nutzen zukünftiger Leser:

Ich bin auf das folgende Video gestoßen, in dem Compile und Link in Angular JS auf sehr gute Weise erklärt werden:

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

Es wäre nicht erfreulich, den gesamten Inhalt hier zu kopieren / einzugeben. Ich habe ein paar Screenshots aus dem Video gemacht, die alle Phasen der Kompilierungs- und Linkphase erklären:

Kompilieren und verknüpfen Sie in Angular JS

Kompilieren und verknüpfen in Angular JS - Verschachtelte Direktiven

Der zweite Screenshot ist etwas verwirrend. Wenn wir jedoch der Schrittnummerierung folgen, ist dies recht einfach.

Erster Zyklus: "Kompilieren" wird zuerst für alle Anweisungen ausgeführt.
Zweiter Zyklus: "Controller" und "Pre-Link" werden ausgeführt (kurz nacheinander) Dritter Zyklus: "Post-Link" wird in umgekehrter Reihenfolge ausgeführt (beginnend von innen)

Das Folgende ist der Code, der das Obige demonstriert:

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

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

}]);

app.directive ('message', function ($ interpolate) {
    Rückkehr{

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

                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>

AKTUALISIEREN:

Teil 2 desselben Videos ist hier verfügbar: https://www.youtube.com/watch?v=1M3LZ1cu7rw In diesem Video wird in einem einfachen Beispiel erläutert, wie Sie DOM ändern und Ereignisse während des Kompilierungs- und Verknüpfungsprozesses von Angular JS behandeln .


Gebrauchte compileund posteinen DOM zu ändern , bevor dass es eingeschaltet ist zu modifizieren templateTeil von einer Anbieter - Richtlinie.
Jedi

6

Zwei Phasen: Kompilieren und Verknüpfen

Kompilieren:

Durchsuchen Sie den DOM-Baum nach Anweisungen (Elemente / Attribute / Klassen / Kommentare). Jede Zusammenstellung einer Direktive kann ihre Vorlage oder ihren Inhalt ändern, der noch nicht kompiliert wurde. Sobald eine Direktive übereinstimmt, gibt sie eine Verknüpfungsfunktion zurück, die in einer späteren Phase zum Verknüpfen von Elementen verwendet wird. Am Ende der Kompilierungsphase haben wir eine Liste der kompilierten Direktiven und ihrer entsprechenden Verknüpfungsfunktionen.

Verknüpfung:

Wenn ein Element verknüpft ist, wird der DOM-Baum an seinem Verzweigungspunkt im DOM-Baum unterbrochen, und der Inhalt wird durch die kompilierte (und verknüpfte) Instanz der Vorlage ersetzt. Der ursprünglich verschobene Inhalt wird entweder verworfen oder im Falle einer Transklusion wieder mit der Vorlage verknüpft. Bei der Transklusion werden die beiden Teile wieder miteinander verbunden (ähnlich einer Kette, wobei sich das Schablonenstück in der Mitte befindet). Wenn die Verknüpfungsfunktion aufgerufen wird, wurde die Vorlage bereits an einen Bereich gebunden und als untergeordnetes Element des Elements hinzugefügt. Die Link-Funktion ist Ihre Möglichkeit, das DOM weiter zu manipulieren und Änderungs-Listener einzurichten.


3

Diese Frage ist alt von Ich möchte eine kurze Zusammenfassung machen, die helfen kann:

  • Kompilierung wird einmal für alle Direktiveninstanzen aufgerufen
  • Der Hauptzweck des Kompilierens besteht darin, die Linkfunktion (und möglicherweise die Pre / Post) -Funktion / das Objekt zurückzugeben / zu erstellen. Sie können auch Inhalte initiieren, die von Instanzen der Direktive gemeinsam genutzt werden.
  • Meiner Meinung nach ist "Link" ein verwirrender Name für diese Funktion. Ich würde "Pre-Rendering" bevorzugen.
  • Der Link wird für jede Instanz einer Direktive aufgerufen und dient dazu, das Rendern der Direktive im DOM vorzubereiten.

1
ein Plus für den Vorschlagsnamen: "Pre-Render"
Hailong Cao
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.