AngularJS의 컴파일 기능과 링크 기능의 차이점은 무엇입니까?


208

누군가 간단한 용어로 설명 할 수 있습니까?

문서가 약간 둔해 보입니다. 나는 한 번에 다른 것을 사용할 때의 본질과 큰 그림을 얻지 못합니다. 이 두 가지를 대조하는 예는 대단합니다.


2
지시어 함수에 대한보다 포괄적 인 개요 : 각도 지시어-컴파일, 컨트롤러, 사전 링크 및 사후 링크 사용시기 .
Izhaki

답변:


217
  • 컴파일 함수- 템플릿 DOM 조작 (예 : tElement = 템플릿 요소 조작)에 사용되므로 지시문과 관련된 템플릿의 모든 DOM 복제본에 적용됩니다.

  • 링크 기능- 인스턴스 DOM 조작 (예 : iElement = 개별 인스턴스 요소 조작 )뿐만 아니라 DOM 리스너 (예 : 인스턴스 범위의 $ watch 표현식)를 등록하는 데 사용합니다 .
    템플릿이 복제 된 후에 실행됩니다. 예를 들어, <li-ng-repeat ...> 내에서 링크 함수는 <li> 템플릿 (tElement)이 특정 <li> 요소에 대해 (iElement로) 복제 된 후에 실행됩니다.
    $ watch ()를 사용하면 지시문에 인스턴스 범위 속성 변경 사항 (인스턴스 범위가 각 인스턴스와 연결됨)을 알릴 수 있으며, 지시문은 인스턴스 범위의 내용을 인스턴스 범위로 복사하여 업데이트 된 인스턴스 값을 DOM으로 렌더링 할 수 있습니다. DOM.

DOM 변환은 컴파일 기능 및 / 또는 링크 기능에서 수행 될 수 있습니다.

대부분의 지시문은 링크 함수 만 필요합니다. 대부분의 지시문은 특정 DOM 요소 인스턴스 (및 해당 인스턴스 범위) 만 처리하기 때문입니다.

사용할 것을 결정하는 데 도움이되는 한 가지 방법 : 컴파일 함수가 scope인수를 받지 않는다는 것을 고려하십시오 . (전혀 범위를받는 transclude 연결 함수 인수를 의도적으로 무시하고 있습니다. 이는 거의 사용 되지 않습니다 .) 따라서 컴파일 함수는 (인스턴스) 범위가 필요한 작업을 수행 할 수 없습니다. '모델 / 인스턴스 범위 속성을 보지 않고 인스턴스 범위 정보를 사용하여 DOM을 조작 할 수 없으며 인스턴스 범위에 정의 된 함수를 호출 할 수 없습니다.

그러나 링크 함수와 같은 컴파일 함수는 속성에 액세스 할 수 있습니다. 따라서 DOM 조작에 인스턴스 범위가 필요하지 않으면 컴파일 함수를 사용할 수 있습니다. 이러한 이유로 컴파일 함수 만 사용하는 지시문 의 예 는 다음과 같습니다 . 속성을 검사하지만 작업을 수행하기 위해 인스턴스 범위가 필요하지 않습니다.

다음 은 컴파일 함수 만 사용하는 지시문 의 예 입니다. 지시문은 템플릿 DOM 만 변환하면되므로 컴파일 함수를 사용할 수 있습니다.

사용할 함수를 결정하는 또 다른 방법 : 링크 함수에서 "element"매개 변수를 사용하지 않으면 링크 함수가 필요하지 않습니다.

대부분의 지시문에는 링크 기능이 있으므로 예제를 제공하지 않겠습니다. 찾기가 매우 쉬워야합니다.

컴파일 함수 및 링크 함수 (또는 사전 및 사후 링크 함수)가 필요한 경우 '컴파일'속성이 정의 된 경우 '링크'속성이 무시되므로 컴파일 함수는 링크 함수를 리턴해야합니다.

또한보십시오


5
컴파일 대 링크에 대한 최고의 설명.
Nexus23

1
당신이 말할 때 if you don't use the "element" parameter in the link function, then you probably don't need a link function.대신 "요소"의 "범위"를 의미합니까?
Jason Larke

69

나는 며칠 동안 벽에 머리를 대고 조금 더 설명이 필요하다고 느낍니다.

기본적으로 문서에서는 분리가 주로 성능 향상이라고 언급합니다. 하위 요소 자체가 컴파일되기 전에 DOM을 수정해야 할 때 컴파일 단계가 주로 사용된다는 것을 반복합니다.

우리의 목적을 위해, 나는 용어를 강조 할 것이다.

컴파일러 SERVICE ($ compile)는 DOM을 처리하고 지시문에서 다양한 코드 비트를 실행하는 각도 메커니즘입니다.

컴파일 FUNCTION은 지시문 내의 한 비트의 코드이며, 컴파일러 SERVICE ($ compile)에 의해 특정 시간에 실행됩니다.

컴파일 기능에 대한 참고 사항 :

  1. ROOT 요소 (지시문이 영향을받는 요소)는 이미 DOM의 외부 레벨에서 컴파일 중이므로 (컴파일 SERVICE가 이미 해당 요소에서 지시문을 스캔 했으므로) 수정할 수 없습니다.

  2. (중첩 된) 요소에 다른 지시문을 추가하려면 다음 중 하나를 수행하십시오.

    1. 컴파일 단계에서 추가해야합니다.

    2. 컴파일 서비스를 연결 단계에 삽입하고 요소를 수동으로 컴파일해야합니다. 그러나 무언가를 두 번 컴파일하는 것을 조심하십시오!

$ compile에 대한 중첩 및 명시 적 호출이 작동하는 방법을 확인하는 것도 도움이되므로 http://jsbin.com/imUPAMoV/1/edit 에서 놀이터를 만들었습니다 . 기본적으로 단지 단계를 console.log에 기록합니다.

여기에 해당 저장소에 표시되는 결과가 나와 있습니다. 사용자 지정 지시문 tp 및 sp의 DOM의 경우 다음과 같이 중첩됩니다.

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

각도 컴파일 서비스는 다음을 호출합니다.

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을 통해 확인할 수 있음).

  1. 컴파일 서비스가 내 필드를 찾습니다.
  2. 지시문에서 컴파일 FUNCTION을 호출하여 DOM을 업데이트합니다.
  3. 그런 다음 컴파일 서비스는 결과 DOM과 COMPILES (재귀 적으로)로 들어갑니다.
  4. 그런 다음 컴파일 서비스는 사전 링크 하향식을 호출합니다.
  5. 그런 다음 컴파일 서비스는 사후 링크 BOTTOM UP을 호출하므로 내 필드의 링크 기능을 내부 노드가 연결된 후라고합니다.

위의 예에서 모든 지시문의 작업은 컴파일 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);
    }
});

$ compile SERVICE에 전달하는 요소에 원래 지시어가 없다고 확신하는 경우 (예 : 사용자가 정의한 템플릿에서 왔거나 angular.element ()로 방금 생성 한 경우) 최종 결과는 거의 같습니다 이전과 동일합니다 (일부 작업을 반복 할 수 있음). 그러나 요소에 다른 지시문이있는 경우 명령을 다시 처리하여 모든 종류의 비정상적인 동작 (예 : 이벤트 및 시계의 이중 등록)이 발생할 수 있습니다.

따라서 컴파일 단계는 매크로 스타일 작업에 훨씬 적합한 선택입니다.

시나리오 2 : 범위 데이터를 통한 DOM 구성

이것은 위의 예에서 따릅니다. DOM을 조작하는 동안 범위에 액세스해야한다고 가정하십시오. 이 경우 컴파일 섹션은 범위를 사용할 수 있기 전에 발생하므로 쓸모가 없습니다.

따라서 유효성 검사를 통해 입력을 포주로 만들고 서버 쪽 ORM 클래스 (DRY)에서 유효성 검사를 내보내고 자동으로 적용하여 해당 유효성 검사에 적합한 클라이언트 쪽 UI를 생성하려고한다고 가정하겠습니다.

모델은 다음을 추진할 수 있습니다.

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>

다양한 유효성 검사 오류를 표시하기 위해 적절한 지시문과 div를 자동으로 포함하는 방법 :

<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과 연결하여 액세스 할 수 있습니다) .

이 경우 컴파일 함수를 작성할 필요가 없습니다. 링크는 정말로 당신이 원하는 것입니다. 단계는 다음과 같습니다.

  1. 각도 지시문이 완전히없는 템플릿을 정의하십시오.
  2. 다양한 속성을 추가하는 링크 함수 정의
  3. 최상위 요소 (my-field 지시문)에서 허용 할 수있는 각도 지시문을 제거하십시오. 이미 처리되었으므로 이중 처리되지 않도록하는 방법입니다.
  4. 최상위 요소에서 컴파일 서비스를 호출하여 완료

이렇게 :

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
굉장하고 멋진 답변!
Nexus23

이중 컴파일없이 중첩 된 요소를 추가하는 방법은 무엇입니까?
Art713

50

문서에서 :

컴파일러

컴파일러는 속성을 찾는 DOM을 통과하는 각도 서비스입니다. 컴파일 과정은 두 단계로 이루어집니다.

  1. 컴파일 : DOM을 순회하고 모든 지시문을 수집하십시오. 결과는 연결 기능입니다.

  2. 링크 : 지시문을 범위와 결합하고 라이브 뷰를 생성합니다. 범위 모델의 모든 변경 사항이 뷰에 반영되고 뷰와의 사용자 상호 작용이 범위 모델에 반영됩니다. 스코프 모델을 단일 진실 소스로 만듭니다.

일부 지시문 ng-repeat은 컬렉션의 각 항목에 대해 DOM 요소를 한 번 복제합니다. 컴파일 및 링크 단계를 수행하면 복제 된 템플릿을 한 번만 컴파일 한 다음 각 복제 인스턴스마다 한 번만 링크하면되므로 성능이 향상됩니다.

따라서 적어도 어떤 경우 에는 두 단계가 최적화로 별도로 존재합니다.


@ UmurKontacı에서 :

DOM 변환을하려는 경우이어야합니다 compile. 동작이 변경되는 일부 기능을 추가하려면이 기능이에 있어야합니다 link.


46
당신이 화장에가는 경우 DOM변환, 그것은해야 compile일부 기능이에서해야한다, 행동의 변화입니다 추가하려는 경우 link.
Umur Kontacı

4
위의 의견에 +1; 이것은 내가 지금까지 찾은 가장 간결한 설명입니다. 여기 에서 찾은 자습서와 일치 합니다 .
베니 보 테마

18

이것은 지시에 관한 Misko의 이야기에서 나온 것입니다. http://youtu.be/WqmeI5fZcho?t=16m23s

컴파일러 함수를 템플릿에서 작동하는 것과 템플릿에 클래스를 추가하는 등 템플릿 자체를 변경할 수있는 것으로 생각하십시오. 그러나 연결 기능은 범위에 액세스 할 수 있고 특정 템플릿의 각 인스턴스화에 대해 한 번 실행되는 연결 기능이기 때문에 실제로 두 기능을 바인딩하는 연결 기능입니다. 따라서 컴파일 함수 내에 배치 할 수있는 유일한 것은 모든 인스턴스에서 공통적 인 것입니다.


10

실에 조금 늦었다. 그러나 미래 독자들의 이익을 위해 :

Angular JS의 컴파일 및 링크를 매우 훌륭한 방식으로 설명하는 다음 비디오를 보았습니다.

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

여기에 모든 내용을 복사 / 입력하는 것이 좋지 않을 것입니다. 비디오에서 스크린 샷을 몇 장 가져 와서 컴파일 및 링크 단계의 모든 단계를 설명했습니다.

Angular JS의 컴파일 및 링크

Angular JS의 컴파일 및 링크-중첩 지시문

두 번째 스크린 샷은 약간 혼란 스럽습니다. 그러나 단계 번호 매기기를 따르는 경우에는 매우 간단합니다.

첫 번째 사이클 : 모든 지시문에 대해 "컴파일"이 먼저 수행됩니다.
두 번째주기 : "컨트롤러"및 "사전 링크"가 수행됩니다 (순서대로) 세 번째주기 : "사후 링크"가 역순으로 수행됩니다 (가장 안쪽부터 시작)

다음은 위의 코드를 보여줍니다.

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

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

}]);

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

        컴파일 : function (tElement, tAttributes) { 
            console.log (tAttributes.text + "-컴파일 중." ");
            {

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

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

            }
        },

        컨트롤러 : function ($ scope, $ element, $ attrs) {
            console.log ($ attrs.text + "-제어기에서 ..");
        },

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

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

최신 정보:

동일한 비디오의 2 부 : https://www.youtube.com/watch?v=1M3LZ1cu7rw 이 비디오는 간단한 예제에서 Angular JS의 컴파일 및 링크 프로세스 중에 DOM을 수정하고 이벤트를 처리하는 방법에 대해 자세히 설명합니다. .


공급 업체 지시문 에서 부분적으로 수정하기 전에 DOM을 사용 compile하고 post수정합니다 template.
jedi

6

두 단계 : 컴파일 및 링크

엮다:

지시문 (요소 / 속성 / 클래스 / 주석)을 찾는 DOM 트리를 탐색하십시오. 지시문을 컴파일 할 때마다 템플릿을 수정하거나 아직 컴파일되지 않은 내용을 수정할 수 있습니다. 지시어가 일치하면 연결 함수를 반환하며,이 함수는 이후 단계에서 요소를 서로 연결하는 데 사용됩니다. 컴파일 단계가 끝나면 컴파일 된 지시문과 해당 연결 함수의 목록이 있습니다.

링크:

요소가 링크되면 DOM 트리는 DOM 트리의 분기점에서 끊어지고 컨텐츠는 템플리트의 컴파일 된 (및 링크 된) 인스턴스로 대체됩니다. 원래 변위 된 컨텐츠는 버려지거나 변환의 경우 템플리트로 다시 링크됩니다. 번역을 사용하면 두 조각이 다시 연결됩니다 (템플릿 조각이 가운데에있는 체인과 같은 종류). 링크 함수가 호출되면 템플릿은 이미 범위에 바인딩되어 있고 요소의 자식으로 추가되었습니다. 링크 기능은 DOM을 더 조작하고 변경 리스너를 설정할 수있는 기회입니다.


3

이 질문은 오래되었습니다. 약식 요약을 작성하고 싶습니다.

  • 모든 지시문 인스턴스에 대해 한 번 호출 된 컴파일
  • 컴파일의 주요 목적은 링크 (및 가능하면 사전 / 사후) 기능 / 개체를 반환 / 생성하는 것입니다. 지시문 인스턴스간에 공유되는 항목을 초기화 할 수도 있습니다.
  • 제 생각에는 "링크"는이 기능의 혼동되는 이름입니다. "사전 렌더링"을 선호합니다.
  • 링크는 각 지시문 인스턴스에 대해 호출되며 그 목적은 DOM에서 지시문의 렌더링을 준비하는 것입니다.

1
제안 이름에 대한 하나의 플러스 : "사전 렌더링"
Hailong Cao
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.