Spec-Zone.ru › Angular.js 1.5

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

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

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

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

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

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

  • Области видимости предоставляют контекст, в котором вычисляются выражения. Например, {{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!');
});

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Когда браузер вызывает JavaScript, код выполняется вне контекста выполнения Angular, а значит, Angular не знает о модификациях модели. Для правильной обработки изменений модели выполнение должно войти в контекст выполнения Angular с помощью метода $apply. Только изменения модели, которые выполняются внутри метода $apply, будут должным образом учтены Angular. Например, если директива слушает события 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 Angular делают это неявно, поэтому дополнительный вызов $apply не нужен при выполнении синхронной работы в контроллерах или асинхронной работы с сервисами $http, $timeout или $interval.

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

    В конце $apply, Angular выполняет цикл $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.

Области видимости $watch – соображения по производительности

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

Глубина наблюдения scope $watch

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

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

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

Диаграмма и пример ниже описывают взаимодействие Angular с циклом обработки событий браузера.

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

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

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

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

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

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

Spec-Zone.ru

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