Spec-Zone.ru › Angular.js 1.6

Улучшить эту документациюЧто такое области видимости?

Область видимости — это объект, который ссылается на модель приложения. Это контекст выполнения для выражений. Области видимости организованы в иерархическую структуру, которая имитирует структуру DOM приложения. Области видимости могут отслеживать выражения и распространять события.

Характеристики области видимости

  • Области видимости предоставляют API ($watch), чтобы наблюдать за изменениями модели.

  • Области видимости предоставляют API ($apply), чтобы распространять любые изменения модели через систему в представление извне «мира AngularJS» (контроллеры, сервисы, обработчики событий AngularJS).

  • Области видимости могут быть вложены, чтобы ограничить доступ к свойствам компонентов приложения, одновременно предоставляя доступ к общим свойствам модели. Вложенные области видимости являются либо «дочерними областями видимости», либо «изолированными областями видимости». «Дочерняя область видимости» (прототипически) наследует свойства от родительской области видимости. «Изолированная область видимости» — нет. См. изолированные области видимости для получения дополнительной информации.

  • Области видимости предоставляют контекст, в котором оцениваются выражения. Например, {{username}} выражение бессмысленно, если оно не оценивается относительно конкретной области видимости, которая определяет свойство username.

Область видимости как модель данных

Область видимости — это связь между контроллером приложения и представлением. Во время фазы связывания шаблона директивы устанавливают $watch выражения в области видимости. $watch позволяет директивам получать уведомления об изменениях свойств, что позволяет директиве отображать обновленное значение в DOM.

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

В приведенном выше примере обратите внимание, что MyController присваивает World свойству username области видимости. Затем область видимости уведомляет input об этом присвоении, которое затем отображает входное поле с предварительно заполненным именем пользователя. Это демонстрирует, как контроллер может записывать данные в область видимости.

Аналогично, контроллер может присваивать поведению область видимости, как видно из метода sayHello, который вызывается при нажатии пользователем кнопки «приветствовать». Метод sayHello может считывать свойство username и создавать свойство greeting. Это демонстрирует, что свойства области видимости автоматически обновляются, когда они связаны с HTML-элементами ввода.

Логически отображение {{greeting}} включает:

  • получение области видимости, связанной с узлом DOM, где {{greeting}} определено в шаблоне. В этом примере это та же область видимости, что и область видимости, переданная в MyController. (Мы рассмотрим иерархии областей видимости позже.)

  • оценить greeting выражение относительно полученной выше области видимости и присвоить результат тексту содержащего DOM-элемента.

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

С точки зрения тестирования разделение контроллера и представления желательно, поскольку это позволяет нам тестировать поведение, не отвлекаясь на детали отображения.

it('should say hello', function() {
  var scopeMock = {};
  var cntl = new MyController(scopeMock);

  // Assert that username is pre-filled
  expect(scopeMock.username).toEqual('World');

  // Assert that we read new username and greet
  scopeMock.username = 'angular';
  scopeMock.sayHello();
  expect(scopeMock.greeting).toEqual('Hello angular!');
});

Иерархии областей видимости

В каждом приложении AngularJS есть ровно одна корневая область видимости, но может быть несколько дочерних областей видимости.

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

Когда AngularJS оценивает {{name}}, он сначала проверяет область видимости, связанную с заданным элементом, на наличие свойства name. Если такого свойства не найдено, он ищет его в родительской области видимости и так далее, пока не достигнет корневой области видимости. В JavaScript это поведение известно как прототипное наследование, и дочерние области видимости прототипически наследуют от своих родителей.

Этот пример иллюстрирует области видимости в приложении и прототипное наследование свойств. За примером следует диаграмма, отображающая границы областей видимости.

Обратите внимание, что AngularJS автоматически размещает класс ng-scope на элементах, где прикреплены области видимости. Определение <style> в этом примере выделяет красным новые места размещения области видимости. Дочерние области видимости необходимы, потому что повторитель оценивает {{name}} выражение, но в зависимости от области видимости, в которой оценивается выражение, он дает разные результаты. Аналогично, оценка {{department}} прототипически наследует от корневой области видимости, так как это единственное место, где определено свойство department.

Получение областей видимости из DOM

Области видимости прикрепляются к DOM как свойство данных $scope, и их можно получить для отладки. (Вряд ли в приложении потребуется получать области видимости таким образом.) Место, где корневая область видимости прикреплена к DOM, определяется местоположением директивы ng-app. Обычно ng-app размещается на элементе <html>, но его можно разместить и на других элементах, если, например, только часть представления должна контролироваться AngularJS.

Чтобы проверить область видимости в отладчике:

  1. Наведите указатель мыши на интересующий элемент в браузере и выберите «Просмотреть элемент». Вы увидите отладчик браузера с выделенным элементом, на который вы нажали.

  2. Отладчик позволяет получить доступ к текущему выбранному элементу в консоли как к переменной $0.

  3. Чтобы получить связанную область видимости в консоли, выполните команду: angular.element($0).scope() или просто введите $scope

Распространение событий области видимости

Области видимости могут распространять события аналогично событиям DOM. Событие может быть рассылано дочерним областям видимости или выдано родительским областям видимости.

Жизненный цикл области видимости

Обычный поток браузера при получении события заключается в выполнении соответствующего обратного вызова JavaScript. После завершения обратного вызова браузер повторно отображает DOM и возвращается к ожиданию новых событий.

Когда браузер вызывает JavaScript, код выполняется вне контекста выполнения AngularJS, что означает, что AngularJS не знает об изменениях модели. Для правильной обработки изменений модели выполнение должно войти в контекст выполнения AngularJS с помощью метода $apply. Только изменения модели, выполняемые внутри метода $apply, будут должным образом учтены AngularJS. Например, если директива прослушивает события DOM, такие как ng-click, она должна вычислять выражение внутри метода $apply.

После вычисления выражения, метод $apply выполняет $digest. На фазе $digest область видимости проверяет все $watch выражения и сравнивает их со значением предыдущего цикла. Эта проверка на изменения выполняется асинхронно. Это означает, что присвоение, такое как $scope.username="angular", не приведет к немедленному уведомлению $watch, вместо этого уведомление $watch задерживается до фазы $digest. Эта задержка желательна, так как она объединяет несколько обновлений модели в одно уведомление $watch, а также гарантирует, что во время уведомления $watch не выполняются другие $watchы. Если $watch изменяет значение модели, это заставит запуститься дополнительный цикл $digest.

  1. Создание

    Корневая область видимости создается во время загрузки приложения с помощью $injector. Во время связывания шаблона некоторые директивы создают новые дочерние области видимости.

  2. Регистрация наблюдателей

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

  3. Изменение модели

    Для правильного наблюдения за изменениями, вы должны выполнять их только в scope.$apply(). API AngularJS делают это неявно, поэтому дополнительный вызов $apply не нужен при выполнении синхронной работы в контроллерах или асинхронной работе с $http, $timeout или $interval сервисами.

  4. Наблюдение за изменениями

    В конце $apply, AngularJS выполняет цикл $digest в корневой области видимости, который затем распространяется по всем дочерним областям видимости. Во время цикла $digest, все $watch выражения или функции проверяются на изменения модели, и если изменение обнаружено, вызывается слушатель $watch.

  5. Уничтожение области видимости

    Когда дочерние области видимости больше не нужны, создатель дочерней области видимости отвечает за их уничтожение через API scope.$destroy(). Это остановит распространение вызовов $digest в дочернюю область видимости и позволит сборщику мусора освободить память, используемую дочерними моделями области видимости.

Области видимости и директивы

Во время фазы компиляции компилятор сопоставляет директивы с шаблоном DOM. Директивы обычно делятся на две категории:

  • Наблюдая за директивами, такими как выражения с двойными фигурными скобками {{expression}}, регистрируйте слушателей с помощью метода $watch(). Этот тип директивы должен уведомляться всякий раз, когда выражение изменяется, чтобы она могла обновить представление.

  • Директивы-слушатели, такие как ng-click, регистрируют слушателя в DOM. Когда слушатель DOM срабатывает, директива выполняет связанное выражение и обновляет представление с помощью метода $apply().

При получении внешнего события (например, действия пользователя, таймера или XHR) связанное выражение должно быть применено к области видимости через метод $apply(), чтобы все слушатели были обновлены правильно.

Директивы, создающие области видимости

В большинстве случаев директивы и области видимости взаимодействуют, но не создают новые экземпляры области видимости. Однако некоторые директивы, такие как ng-controller и ng-repeat, создают новые дочерние области видимости и прикрепляют дочернюю область видимости к соответствующему элементу DOM. Вы можете получить доступ к области видимости для любого элемента DOM, используя вызов метода angular.element(aDomElement).scope(). См. руководство по директивам для получения дополнительной информации об изолированных областях видимости.

Контроллеры и области видимости

Области видимости и контроллеры взаимодействуют в следующих ситуациях:

  • Контроллеры используют области видимости для экспонирования методов контроллера в шаблонах (см. ng-controller).

  • Контроллеры определяют методы (поведение), которые могут изменять модель (свойства в области видимости).

  • Контроллеры могут регистрировать наблюдения за моделью. Эти наблюдения выполняются сразу после выполнения поведения контроллера.

См. ng-controller для получения дополнительной информации.

Соображения по производительности наблюдения за областью видимости

Определение изменений свойств области видимости — распространенная операция в AngularJS, поэтому функция проверки изменений должна быть эффективной. Следует позаботиться о том, чтобы функция проверки изменений не выполняла доступ к DOM, так как доступ к DOM на порядки медленнее, чем доступ к свойствам объекта JavaScript.

Глубина наблюдения за областью видимости

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

  • Наблюдение по ссылке (scope.$watch (watchExpression, listener)) обнаруживает изменение, когда все значение, возвращаемое выражением наблюдения, переключается на новое значение. Если значение представляет собой массив или объект, изменения внутри него не обнаруживаются. Это самая эффективная стратегия.
  • Наблюдение содержимого коллекции (scope.$watchCollection (watchExpression, listener)) обнаруживает изменения, происходящие внутри массива или объекта: добавление, удаление или переупорядочение элементов. Обнаружение является поверхностным — оно не достигает вложенных коллекций. Наблюдение содержимого коллекции дороже, чем наблюдение по ссылке, потому что необходимо сохранять копии содержимого коллекции. Однако стратегия пытается свести к минимуму количество копирования.
  • Наблюдение по значению (scope.$watch (watchExpression, listener, true)) обнаруживает любые изменения в произвольно вложенной структуре данных. Это самая мощная стратегия обнаружения изменений, но также и самая дорогостоящая. При каждом переваривании требуется полное обход вложенной структуры данных, а ее полная копия должна храниться в памяти.

Интеграция с циклом событий браузера

На диаграмме и в примере ниже показано, как AngularJS взаимодействует с циклом событий браузера.

  1. Цикл событий браузера ожидает поступления события. Событие — это взаимодействие пользователя, событие таймера или сетевое событие (ответ от сервера).
  2. Вызывается обработчик события. Это входит в контекст JavaScript. Обработчик может изменить структуру DOM.
  3. После выполнения обработчика браузер выходит из контекста JavaScript и повторно отображает представление на основе изменений DOM.

AngularJS изменяет обычный поток JavaScript, предоставляя свой собственный цикл обработки событий. Это разделяет JavaScript на классический и контекст выполнения AngularJS. Только операции, которые выполняются в контексте выполнения AngularJS, получат выгоду от привязки данных AngularJS, обработки исключений, наблюдения за свойствами и т. д. Вы также можете использовать $apply(), чтобы перейти в контекст выполнения AngularJS из JavaScript. Имейте в виду, что во многих местах (контроллеры, сервисы) $apply уже был вызван для вас директивой, обрабатывающей событие. Явное обращение к $apply необходимо только при реализации пользовательских обработчиков событий или при работе с обработчиками сторонних библиотек.

  1. Переход в контекст выполнения AngularJS путем вызова scope.$apply(stimulusFn), где stimulusFn — это работа, которую вы хотите выполнить в контексте выполнения AngularJS.
  2. AngularJS выполняет stimulusFn(), что обычно изменяет состояние приложения.
  3. AngularJS входит в цикл $digest. Цикл состоит из двух меньших циклов, которые обрабатывают очередь $evalAsync и список $watch. Цикл $digest продолжает итерироваться до тех пор, пока модель не стабилизируется, то есть очередь $evalAsync пуста, а список $watch не обнаруживает изменений.
  4. Очередь $evalAsync используется для планирования работы, которая должна выполняться вне текущей области видимости, но до рендеринга представления браузером. Это обычно делается с помощью setTimeout(0), но подход setTimeout(0) отличается медленностью и может вызвать мерцание представления, так как браузер рендерит представление после каждого события.
  5. Список $watch — это набор выражений, которые могли измениться с момента последней итерации. Если изменение обнаружено, вызывается функция $watch, которая обычно обновляет DOM новым значением.
  6. По завершении цикла AngularJS $digest выполнение выходит из контекста AngularJS и контекста JavaScript. За этим следует повторный рендеринг DOM браузером для отражения любых изменений.

Вот объяснение того, как пример Hello world достигает эффекта привязки данных при вводе текста в текстовое поле.

  1. Во время компиляции:
    1. директивы ng-model и input устанавливают слушателя keydown на элементе управления <input>.
    2. интерполяция устанавливает $watch для уведомления об изменениях name.
  2. Во время выполнения:
    1. Нажатие клавиши 'X' заставляет браузер генерировать событие keydown на элементе управления ввода.
    2. Директива input обрабатывает изменение значения ввода и вызывает $apply("name = 'X';") для обновления модели приложения в контексте выполнения AngularJS.
    3. AngularJS применяет изменения name = 'X'; к модели.
    4. Начинается цикл $digest
    5. Список $watch обнаруживает изменение свойства name и уведомляет интерполяцию, которая в свою очередь обновляет DOM.
    6. AngularJS выходит из контекста выполнения, что, в свою очередь, выходит из события keydown и с ним из контекста выполнения JavaScript.
    7. Браузер повторно отображает представление с обновленным текстом.

© 2010–2018 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://code.angularjs.org/1.6.9/docs/guide/scope

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API