javascript - 자바스크립트 - AngularJS에서 데이터 바인딩은 어떻게 작동합니까?




자바스크립트 데이터 바인딩 (10)

AngularJS 프레임 워크에서 데이터 바인딩은 어떻게 작동합니까?

나는 그들의 사이트 에서 기술적 세부 사항을 발견하지 못했다. 데이터가 뷰에서 모델로 전파 될 때 작동하는 방식이 다소 명확합니다. 그러나 AngularJS는 setter 및 getter없이 모델 속성의 변경을 어떻게 추적합니까?

이 작업을 수행 할 수있는 JavaScript 전문가 가 있음을 발견했습니다. 그러나 Internet Explorer 6Internet Explorer 7 에서는 지원되지 않습니다. 그렇다면 AngularJS는 내가 예를 들어 다음과 같이 변경하고이 변경 사항을보기에 반영했다는 것을 어떻게 알 수 있습니까?

myobject.myproperty="new value";

$scope 객체를 더티 검사하여

Angular는 $scope 객체에 간단한 관찰자 array 을 유지합니다. $scope 를 검사하면 $$watchers 라는 array 이 있다는 것을 알 수 있습니다.

각 관찰자는 다른 것들과

  1. 감시자가 모니터링하는 표현식입니다. 이것은 단지 attribute 이름 일 수도 있고, 좀 더 복잡한 것일 수도 있습니다.
  2. 마지막으로 알려진 식의 값입니다. 이것은 표현식의 현재 계산 된 값과 비교하여 확인할 수 있습니다. 값이 다르면 감시자가 함수를 트리거하고 $scope 를 dirty로 표시합니다.
  3. 감시자가 지저분한 경우 실행될 함수.

관찰자 정의 방법

AngularJS에서 감시자를 정의하는 여러 가지 방법이 있습니다.

  • $scope 에 명시 적으로 $watch attribute$watch 수 있습니다.

    $scope.$watch('person.username', validateUnique);
    
  • 템플릿에 {{}} 보간을 배치 할 수 있습니다 (현재 $scope 에서 보행자가 생성 $scope ).

    <p>username: {{person.username}}</p>
    
  • ng-model 과 같은 지시어에 watcher를 정의하도록 요청할 수 있습니다.

    <input ng-model="person.username" />
    

$digest 주기는 모든 관찰자를 마지막 값과 비교하여 검사합니다.

AngularJS와 정상 채널 (ng-model, ng-repeat 등)을 통해 상호 작용할 때 지침에 의해 다이제스트주기가 트리거됩니다.

다이제스트주기는 $scope 와 모든 자식을 깊이 우선 탐색 합니다. 각 $scope object 에 대해 $$watchers array 반복하고 모든 표현식을 평가합니다. 새 표현식 값이 마지막으로 알려진 값과 다른 경우에는 관찰자의 함수가 호출됩니다. 이 함수는 DOM의 일부를 다시 컴파일하고, $scope 값을 다시 계산하고, AJAX request 트리거하고, 필요한 모든 작업을 수행합니다.

모든 스코프를 통과시키고 모든 시계 표현식을 평가하고 마지막 값과 대조합니다.

감시자가 실행되면 $scope 가 더럽습니다.

감시자가 실행되면 앱은 무언가가 변경된 것을 알고 $scope 는 더티로 표시됩니다.

감시자 함수는 $scope 또는 상위 $scope 에서 다른 속성을 변경할 수 있습니다. 하나의 $watcher 함수가 트리거되면 다른 $scope 가 여전히 깨끗하다는 것을 보장 할 수 없으므로 전체 다이제스트주기를 다시 실행합니다.

AngularJS에는 양방향 바인딩이 있으므로 $scope 트리 위로 데이터를 전달할 수 있기 때문입니다. 이미 소화 된 $scope 의 값을 변경할 수 있습니다. 아마도 우리는 $rootScope 의 값을 변경합니다.

$digest 가 더러 우면 전체 $digest cycle을 다시 실행합니다.

다이제스트주기가 깨끗해질 때까지 ( $watch 식은 모두 이전주기와 동일한 값을가집니다) 또는 다이제스트 제한에 도달 할 때까지 $digest 주기를 반복합니다. 기본적으로이 제한은 10으로 설정됩니다.

다이제스트 제한에 도달하면 AngularJS는 콘솔에 오류를 발생시킵니다.

10 $digest() iterations reached. Aborting!

다이제스트는 컴퓨터에서는 어렵지만 개발자에게는 쉽습니다.

보시다시피 AngularJS 앱에서 변경 될 때마다 AngularJS는 $scope 계층의 모든 단일 관찰자를 확인하여 대응 방법을 확인합니다. 개발자에게는 이것은 엄청난 생산성 향상입니다. 배선 코드를 거의 작성하지 않아도되므로 AngularJS는 값이 변경된 경우에만 알림을 표시하고 나머지 앱은 변경 사항과 일관되게 만듭니다.

머신의 관점에서 볼 때 이것은 매우 비효율적이며 너무 많은 관찰자를 생성하면 앱이 느려질 수 있습니다. Misko는 앱이 구형 브라우저에서 느려지 기 전에 약 4000 명의 관찰자를 인용했습니다.

예를 들어 큰 JSON array 을 통해 ng-repeat 할 경우이 제한에 도달하기 쉽습니다. 일회성 바인딩과 같은 기능을 사용하여 감시자를 만들지 않고 템플릿을 컴파일하면이 문제를 완화 할 수 있습니다.

너무 많은 관찰자를 만드는 것을 피하는 법

사용자가 앱과 상호 작용할 때마다 앱의 모든 단일 관찰자가 적어도 한 번 평가됩니다. AngularJS 앱을 최적화하는 데있어 중요한 부분은 $scope 트리에서 관찰자 수를 줄이는 것입니다. 이를 수행하는 쉬운 방법 중 하나한 번 바인딩하는 것 입니다.

거의 변경되지 않는 데이터가 있으면 다음과 같이 :: 구문을 사용하여 한 번만 바인딩 할 수 있습니다.

<p>{{::person.username}}</p>

또는

<p ng-bind="::person.username"></p>

바인딩은 포함 된 템플릿이 렌더링되고 데이터가 $scope 로드 될 때만 트리거됩니다.

많은 항목이있는 ng-repeat 가있을 때 특히 중요합니다.

<div ng-repeat="person in people track by username">
  {{::person.username}}
</div>

  1. 단방향 데이터 바인딩은 데이터 모델에서 값을 가져 와서 HTML 요소에 삽입하는 방식입니다. 보기에서 모델을 업데이트 할 수있는 방법은 없습니다. 이것은 고전적인 템플릿 시스템에서 사용됩니다. 이러한 시스템은 한 방향으로 만 데이터를 바인딩합니다.

  2. 각도 응용 프로그램의 데이터 바인딩은 모델과 뷰 구성 요소 간의 데이터 자동 동기화입니다.

데이터 바인딩을 통해 모델을 애플리케이션에서 단일 소스로 취급 할 수 있습니다. 뷰는 항상 모델의 투영입니다. 모델이 변경되면 뷰에는 변경 사항이 반영되고 반대의 경우도 마찬가지입니다.


AngularJS는 값을 기억하고 이전 값과 비교합니다. 이것은 기본적인 더티 검사입니다. 값이 변경되면 변경 이벤트가 발생합니다.

AngularJS가 아닌 세계에서 AngularJS로 전환 할 때 호출하는 $apply() 메서드는 $digest() 호출합니다. 소화는 평범한 오래된 더티 검사 일뿐입니다. 모든 브라우저에서 작동하며 완전히 예측 가능합니다.

더티 검사 (AngularJS)와 변경 청취자 ( KnockoutJSBackbone.js ) 비교 : 더티 검사가 간단하고 비효율적 인 것처럼 보일 수도 있지만 (나중에 다룰 예정 임), 항상 의미 상으로 정확하다는 것이 밝혀졌습니다. 변경 청취자에게는 이상한 구석이 많이 있고 의미 론적으로 정확하도록 의존성 추적과 같은 것이 필요합니다. KnockoutJS 의존성 추적은 AngularJS가 가지고 있지 않은 문제에 대한 영리한 기능입니다.

변경 리스너 관련 문제 :

  • 이 구문은 브라우저가 기본적으로 지원하지 않기 때문에 잔인합니다. 예, 프록시가 있지만 모든 경우에 의미 론적으로 정확하지 않으며, 물론 오래된 브라우저에는 프록시가 없습니다. 결론적으로 더티 검사는 POJO 를 수행하는 반면, KnockoutJS 및 Backbone.js는 클래스에서 상속하고 접근자를 통해 데이터에 액세스해야합니다.
  • 유착을 변경하십시오. 항목 배열이 있다고 가정합니다. 추가 할 반복 할 때 배열에 항목을 추가하려는 경우 UI를 렌더링하는 이벤트를 변경시 발생시킬 때마다 추가한다고 가정 해보십시오. 이는 성능에 매우 나쁜 것입니다. 원하는 것은 UI를 한 번만 업데이트하는 것입니다. 변경 이벤트가 너무 세분화되어 있습니다.
  • 변경 청취자는 더 많은 변경 이벤트를 발생시키는 데이터를 추가로 변경할 수 있으므로 변경자가 리스너를 즉시 시작합니다. 스택에서 여러 변경 이벤트가 동시에 발생할 수 있기 때문에 이것은 나쁘다. 어떤 이유로 든 동기화 할 필요가있는 두 개의 배열이 있다고 가정합니다. 하나 또는 둘 다를 추가 할 수 있지만 추가 할 때마다 변경 이벤트가 발생합니다. 변경 이벤트는 현재 세계에 대해 일관성이 없습니다. 이것은 스레드 잠금과 매우 비슷한 문제이며, 각 콜백이 독점적으로 완료 될 때까지 JavaScript가 회피합니다. 변경 이벤트는 setters가 의도하지 않고 명확하지 않은 광범위한 결과를 가져올 수 있으므로 스레드 문제를 다시 발생시킵니다. 원하는 리스너 실행을 지연시키고 한 번에 하나의 리스너 만 실행하므로 모든 코드가 자유롭게 데이터를 변경할 수 있으며 그렇게하는 동안 다른 코드가 실행되지 않는다는 것을 알 수 있습니다. .

실적은 어떻습니까?

더티 검사가 비효율적이기 때문에 우리가 느린 것처럼 보일 수 있습니다. 여기서 우리는 이론적 인 논증을하기보다는 실수를 관찰 할 필요가 있지만 먼저 몇 가지 제약 조건을 정의합시다.

인간은 :

  • 천천히 - 50 밀리 초보다 빠른 것은 사람에게는 지각 할 수 없으므로 "즉각적인"것으로 간주 할 수 있습니다.

  • 제한적 - 한 페이지에 인간에게 약 2000 가지 이상의 정보를 표시 할 수는 없습니다. 그것보다 더 중요한 것은 정말 나쁜 UI이고, 인간은 이것을 처리 할 수 ​​없습니다.

따라서 실제 질문은 다음과 같습니다. 브라우저에서 50ms 동안 얼마나 많은 비교를 할 수 있습니까? 이것은 많은 요소들이 작용함에 따라 대답하기 어려운 질문이지만, 여기에 테스트 케이스가 있습니다 : http://jsperf.com/angularjs-digest/6 , 10,000 명의 관찰자를 생성합니다. 현대적인 브라우저에서는 6 밀리 초 정도 걸립니다. Internet Explorer 8 에서는 약 40ms가 소요됩니다. 보시다시피 요즘은 느린 브라우저에서도 문제가되지 않습니다. 주의 사항 : 시간 제한에 맞추기 위해 비교가 간단해야합니다 ... 안타깝게도 AngularJS에 느린 비교를 추가하기가 너무 쉽기 때문에 느린 응용 프로그램을 쉽게 만들 수 있습니다. 하고있다. 그러나 우리는 계측 모듈을 제공함으로써 답변을 얻길 희망합니다. 이것은 느린 비교인지를 보여줍니다.

비디오 게임과 GPU는 더티 검사 방식을 사용하는 것으로 밝혀졌습니다. 특히 일관성이 있기 때문입니다. 모니터의 재생 빈도 (일반적으로 50-60Hz 또는 16.6-20ms)를 초과하면 성능이 낭비되므로 FPS를 높이는 것보다 더 많은 것을 그리는 것이 좋습니다.


AngularJs는 양방향 데이터 바인딩을 지원합니다.
데이터 보기 -> 컨트롤러컨트롤러 ->보기에 액세스 할 수 있습니다.

예를 들어.

1)

// If $scope have some value in Controller. 
$scope.name = "Peter";

// HTML
<div> {{ name }} </div>

O / P

Peter

ng-model 에서 데이터를 바인딩 할 수 있습니다.
2)

<input ng-model="name" />

<div> {{ name }} </div>

위의 예에서 사용자가 제공 할 입력 내용은 <div> 태그에 표시됩니다.

html에서 컨트롤러로 입력을 바인드하려면 다음을 수행하십시오.
삼)

<form name="myForm" ng-submit="registration()">
   <label> Name </lbel>
   <input ng-model="name" />
</form>

여기에 컨트롤러에서 입력 name 을 사용하려면,

$scope.name = {};

$scope.registration = function() {
   console.log("You will get the name here ", $scope.name);
};

ng-model 은 뷰를 바인딩하고 {{ }} 표현식으로 렌더링합니다.
ng-model 은 사용자가보기에 표시하고 사용자가 상호 작용하는 데이터입니다.
따라서 AngularJs에서 데이터를 쉽게 바인딩 할 수 있습니다.


개인의 데이터 모델을 양식과 연결해야 할 필요가 생겼습니다. 제가 수행 한 작업은 데이터와 양식의 직접 매핑이었습니다.

예를 들어 모델에 다음과 같은 것이있는 경우 :

$scope.model.people.name

양식의 제어 입력 :

<input type="text" name="namePeople" model="model.people.name">

이렇게하면 객체 컨트롤러의 값을 수정하면 뷰에 자동으로 반영됩니다.

모델을 통과 한 예제는 서버 데이터에서 업데이트됩니다. 작성된 쿼리를 기반으로 우편 번호와 우편 번호를 요청하면 해당 뷰와 연관된 식민지와 도시 목록이로드되고 기본적으로 사용자가 첫 번째 값을 설정합니다. 그리고 저는 아주 잘했습니다. 무슨 일이 일어 angularJS 가 모델을 새로 고치는 데 몇 초가 걸리는 경우가 있습니다. 이렇게하려면 데이터를 표시하는 동안 회 전자를 넣을 수 있습니다.


그림으로 설명하기 :

데이터 바인딩에는 매핑이 필요합니다.

범위의 참조가 템플릿의 참조와 정확히 일치하지 않습니다. 두 개의 객체를 데이터 바인딩 할 때는 첫 번째 객체를 듣고 다른 객체를 수정해야하는 세 번째 객체가 필요합니다.

여기에서 <input> 을 수정할 때 data-ref3 을 터치합니다. 고전적인 데이터 바인딩 메카니즘은 데이터 -ref4를 변경 합니다 . 그러면 다른 {{data}} 표현식이 어떻게 움직일 것입니까?

이벤트는 $ digest ()로 연결됩니다.

Angular는 모든 바인딩의 oldValuenewValue 를 유지 관리합니다. 그리고 Angular 이벤트가 끝나면 유명한 $digest() 루프가 WatchList를 검사하여 변경된 사항이 있는지 확인합니다. 이러한 Angular 이벤트ng-click , ng-change , $http completed ...입니다. $digest()oldValuenewValue 와 다른 한 반복됩니다.

앞의 그림에서 data-ref1과 data-ref2가 변경되었음을 알 수 있습니다.

결론

계란과 닭고기와 비슷합니다. 누가 시작하는지 결코 알지 못하지만, 예상대로 대부분의 경우 잘 작동합니다.

다른 점은 메모리와 CPU에서 단순 바인딩의 영향을 쉽게 이해할 수 있다는 것입니다. 바라건대 데스크톱은 이것을 처리 할만큼 충분히 뚱뚱합니다. 휴대 전화가 그렇게 강하지는 않습니다.


다음은 입력 필드를 사용하여 AngularJS를 사용한 데이터 바인딩 예제입니다. 나는 나중에 설명 할 것이다.

HTML 코드

<div ng-app="myApp" ng-controller="myCtrl" class="formInput">
     <input type="text" ng-model="watchInput" Placeholder="type something"/>
     <p>{{watchInput}}</p> 
</div>

AngularJS 코드

myApp = angular.module ("myApp", []);
myApp.controller("myCtrl", ["$scope", function($scope){
  //Your Controller code goes here
}]);

위의 예에서 볼 수 있듯이 AngularJSng-model 을 사용하여 HTML 요소, 특히 input 필드에서 일어나는 일을 듣고 관찰합니다. 무언가가 일어날 때, 무언가를하십시오. 우리의 경우, ng-model 은 콧수염 표기 {{}} 사용하여 우리의 견해에 묶입니다. 입력 필드 안에 입력 된 것이 화면에 즉시 표시됩니다. 그리고 그것은 가장 간단한 형태로 AngularJS를 사용하는 데이터 바인딩의 장점입니다.

희망이 도움이됩니다.

Codepen 에 대한 실제 예제보기


분명히 Scope 주기적으로 검사하는 것은 그것이 첨부 된 Objects에 어떤 변화가 있는지 없는지입니다. 스코프에 연결된 모든 객체가 감시되는 것은 아닙니다. 스코프는 $$ 관찰자를 원형으로 유지합니다. Scope$digest 가 호출 될 때만이 $$watchers 반복합니다.

Angular는 이들 각각에 대한 $ $ 관찰자에게 관찰자를 추가합니다.

  1. {{expression}} - 템플릿에서 (그리고 표현식이있는 곳에서는) 또는 ng-model을 정의 할 때.
  2. $ scope. $ watch ( 'expression / function') - 자바 스크립트에서 watch를위한 angle 객체를 붙일 수 있습니다.

$ watch 함수는 세 개의 매개 변수를 취합니다.

  1. 첫 번째 것은 객체를 반환하는 워처 함수이거나 그냥 표현식을 추가 할 수 있습니다.

  2. 두 번째는 객체에 변화가있을 때 호출 될 리스너 함수입니다. DOM 변경과 같은 모든 기능이이 기능에서 구현됩니다.

  3. 세 번째 매개 변수는 부울을 취하는 선택적 매개 변수입니다. 그것의 진실하고 각진 깊은 시계가 목표 & 그것의 틀린 각 측정하면 다만 목표에 참고를 보는 경우에. $ watch의 거친 구현은 다음과 같습니다.

Scope.prototype.$watch = function(watchFn, listenerFn) {
   var watcher = {
       watchFn: watchFn,
       listenerFn: listenerFn || function() { },
       last: initWatchVal  // initWatchVal is typically undefined
   };
   this.$$watchers.push(watcher); // pushing the Watcher Object to Watchers  
};

Angular에는 Digest Cycle이라는 재미있는 것이 있습니다. $ digest ()는 $ scope. $ digest ()를 호출 한 결과로 시작됩니다. ng-click 지시문을 통해 핸들러 함수에서 $ scope 모델을 변경한다고 가정합니다. 이 경우 AngularJS는 $ digest ()를 호출하여 $ digest주기를 자동으로 트리거합니다 .ng-click 외에도 모델을 변경할 수있는 여러 내장 지시문 / 서비스 (예 : ng-model, $ timeout 등) 자동으로 $ 다이제스트주기를 트리거합니다. $ digest의 거친 구현은 다음과 같습니다.

Scope.prototype.$digest = function() {
      var dirty;
      do {
          dirty = this.$$digestOnce();
      } while (dirty);
}
Scope.prototype.$$digestOnce = function() {
   var self = this;
   var newValue, oldValue, dirty;
   _.forEach(this.$$watchers, function(watcher) {
          newValue = watcher.watchFn(self);
          oldValue = watcher.last;   // It just remembers the last value for dirty checking
          if (newValue !== oldValue) { //Dirty checking of References 
   // For Deep checking the object , code of Value     
   // based checking of Object should be implemented here
             watcher.last = newValue;
             watcher.listenerFn(newValue,
                  (oldValue === initWatchVal ? newValue : oldValue),
                   self);
          dirty = true;
          }
     });
   return dirty;
 };

JavaScript의 setTimeout () 함수를 사용하여 범위 모델을 업데이트하면 Angular는 사용자가 변경할 수있는 것을 알 수 없습니다. 이 경우 우리는 $ apply ()를 수동으로 호출하여 $ 다이제스트주기를 트리거해야합니다. 비슷하게 DOM 이벤트 리스너를 설정하고 핸들러 함수 내에서 일부 모델을 변경하는 지시문이있는 경우 $ apply ()를 호출하여 변경 사항이 적용되도록해야합니다. $ 적용에 대한 큰 아이디어는 Angular를 인식하지 못하는 코드를 실행할 수 있다는 것입니다. 코드는 여전히 범위의 내용을 변경할 수 있습니다. 이 코드를 $ apply에 넣으면 $ digest ()를 호출 할 것입니다. $ apply ()의 거친 구현.

Scope.prototype.$apply = function(expr) {
       try {
         return this.$eval(expr); //Evaluating code in the context of Scope
       } finally {
         this.$digest();
       }
};

데이터 바인딩:

데이터 바인딩이란 무엇입니까?

사용자가보기에서 데이터를 변경할 때마다 범위 모델에서 변경 사항이 업데이트되고 그 반대가 발생합니다.

그게 어떻게 가능해?

짧은 대답 : 다이제스트주기의 도움으로.

설명 : Angular js는 모델에 변경 사항이있는 경우 리스너 함수를 실행하는 범위 모델에서 watcher를 설정합니다.

$scope.$watch('modelVar' , function(newValue,oldValue){

// 새로운 값으로 코드를 업데이트합니다.

});

그러면 언제 어떻게 감시자 함수가 호출됩니까?

감시자 기능은 다이제스트주기의 일부로 호출됩니다.

다이제스트주기는 ng-model, ng-bind, $ timeout, ng-click 및 기타와 같은 지시문 / 서비스로 작성된 각도 j의 일부로 자동으로 트리거됩니다. 다이제스트주기를 트리거 할 수 있습니다.

다이제스트주기 기능 :

$scope.$digest() -> digest cycle against the current scope.
$scope.$apply() -> digest cycle against the parent scope 

$rootScope.$apply()

참고 : $ apply ()는 $ rootScope와 같습니다. $ digest () 더티 검사는 각도 js 응용 프로그램의 모든 하위 $ scope까지 루트 또는 상단 또는 부모 범위에서 시작됩니다.

위의 기능은 응용 프로그램이 각도 js 응용 프로그램인지 확인하여 언급 된 버전의 브라우저 IE에서 작동합니다. 즉, 스크립트 태그에서 참조 된 angularjs 프레임 워크 스크립트 파일을 사용하고 있음을 의미합니다.

고맙습니다.


Angular.js는 우리가 만든 모든 모델에 대한 관찰자를 만듭니다. 모델이 변경 될 때마다 모델에 "ng-dirty"클래스가 추가되므로 감시자는 클래스에 "ng-dirty"클래스가 있고 컨트롤러에서 해당 값을 업데이트하는 모든 모델을 관 {하고 그 반대의 경우도 마찬가지입니다.





data-binding