AngularJS: Разница между методами $ наблюдать и $ смотреть


378

Я знаю, что оба Watchersи Observersвычисляются как только что-то $scopeменяется в AngularJS. Но не мог понять, в чем именно разница между этими двумя.

Мое первоначальное понимание состоит в том, что Observersони вычисляются для угловых выражений, которые являются условиями на стороне HTML, где Watchersвыполняются при выполнении $scope.$watch()функции. Я правильно думаю?


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

@smalone изменился. Спасибо и извините!
Афилаш

👍 Не беспокойся. Спасибо за исправление.
smalone

Ответы:


608

$ наблюдаем () - это методобъекта Attributes , и поэтому он может использоваться только для наблюдения / наблюдения за изменением значения атрибута DOM. Он используется / вызывается только внутри директив. Используйте $ наблюдаем, когда вам нужно наблюдать / смотреть атрибут DOM, который содержит интерполяцию (т. Е. {{}}).
Например,attr1="Name: {{name}}", то в директиве:attrs.$observe('attr1', ...).
(Если вы пытаетесьscope.$watch(attrs.attr1, ...) это не сработает из-за {{}} s - вы получите undefined.) Используйте $ watch для всего остального.

$ watch () более сложный. Он может наблюдать / наблюдать «выражение», где выражение может быть либо функцией, либо строкой. Если выражение является строкой, этофункция $ parse 'd (то есть, оцененная как угловое выражение ) в функцию. (Именно эта функция вызывается в каждом цикле дайджеста.) Строковое выражение не может содержать {{}}. $ watch - это метод на объекта Scope , поэтому его можно использовать / вызывать везде, где у вас есть доступ к объекту области, следовательно, в

  • контроллер - любой контроллер - созданный с помощью ng-view, ng-controller или директивного контроллера
  • связующая функция в директиве, поскольку она также имеет доступ к области видимости

Поскольку строки оцениваются как угловые выражения, $ watch часто используется, когда вы хотите наблюдать / наблюдать свойство model / scope. Например, attr1="myModel.some_prop"затем в функции контроллера или связи: scope.$watch('myModel.some_prop', ...)или scope.$watch(attrs.attr1, ...)(или scope.$watch(attrs['attr1'], ...)).
(Если вы пытаетесьattrs.$observe('attr1') вы получите строку myModel.some_prop, которая, вероятно, не то, что вы хотите.)

Как уже говорилось в комментариях к ответу @ PrimosK, все $ Наблюдения и $ watches проверяются каждый цикл дайджеста .

Директивы с изолированными областями более сложны. Если используется синтаксис '@', вы можете $ наблюдать или $ смотреть атрибут DOM, который содержит интерполяцию (т. Е. {{}}). (Причина, по которой он работает с $ watch, заключается в том, что синтаксис '@' выполняет для нас интерполяцию , поэтому $ watch видит строку без {{}}.) Чтобы упростить запоминание того, что использовать, когда я предлагаю использовать Наблюдать за этим делом тоже.

Чтобы проверить все это, я написал Plunker, в котором определены две директивы. One ( d1) не создает новую область, другой ( d2) создает изолированную область. Каждая директива имеет одинаковые шесть атрибутов. Каждый атрибут является $ наблюдаемым и $ наблюдаемым.

<div d1 attr1="{{prop1}}-test" attr2="prop2" attr3="33" attr4="'a_string'"
        attr5="a_string" attr6="{{1+aNumber}}"></div>

Посмотрите журнал консоли, чтобы увидеть различия между $ наблюдаем и $ наблюдать в функции связывания. Затем нажмите на ссылку и посмотрите, какие $ наблюдатели и $ watches запускаются изменениями свойств, сделанными обработчиком кликов.

Обратите внимание, что при запуске функции ссылки все атрибуты, содержащие {{}}, еще не оцениваются (поэтому, если вы попытаетесь проверить атрибуты, вы получите undefined). Единственный способ увидеть интерполированные значения - это использовать $ наблюдаю (или $ наблюдаем, если используем изолированную область с '@'). Следовательно, получение значений этих атрибутов является асинхронной операцией. (И именно поэтому нам нужны функции $ наблюдений и $ наблюдений.)

Иногда вам не нужно $ наблюдать или смотреть. Например, если ваш атрибут содержит номер или логическое значение ( а не строки), просто оценить его однажды: attr1="22", то, скажем, в вашей связывающей функции: var count = scope.$eval(attrs.attr1). Если это просто константная строка - attr1="my string"- тогда просто используйте attrs.attr1в вашей директиве (нет необходимости в $ eval ()).

См. Также сообщение группы Google Войты о выражениях $ watch.


13
Отличное объяснение! +1
PrimosK

4
Отличный ответ! У вас есть идея, почему ng-src/ng-hrefиспользовать attr.$observeвместо scope.$watchэтого?
Okm

4
+1 За AngularJS Папа! Каждый раз, когда я ищу в Stack информацию о моей последней проблеме с Angular, я неизбежно заканчиваю тем, что читаю @MarkRajcok принятый ответ.
GFoley83

1
Спасибо за отличный пост. scope. $ eval (item) действительно полезен. Если item является строкой json, он преобразуется в объект json.
bnguyen82

5
@tamakisquare, они взаимозаменяемы при использовании @синтаксиса. Я считаю, что нет разницы в производительности (но я не смотрел на фактический исходный код).
Марк Райкок

25

Если я правильно понимаю ваш вопрос, вы спрашиваете, в чем разница, если вы регистрируете обратный вызов слушателя в $watchили если вы делаете это с$observe .

Обратный звонок, зарегистрированный с $watch, запускается, когда$digest выполнении.

Обратный $observeвызов, зарегистрированный в, вызывается при изменении значения атрибутов, которые содержат интерполяцию (например attr="{{notJetInterpolated}}").


Внутри директивы вы можете использовать их оба очень похожим образом:

    attrs.$observe('attrYouWatch', function() {
         // body
    });

или

    scope.$watch(attrs['attrYouWatch'], function() {
         // body
    });

3
На самом деле, поскольку каждое изменение отражается по $digestфазе, можно с уверенностью предположить, что $observeобратный вызов будет вызван $digest. И $watchобратный вызов также будет вызываться, $digestно всякий раз, когда значение изменяется. Я думаю, что они делают ту же самую работу: «наблюдайте за выражением, вызывайте обратный вызов, значение меняется». Разница в ключевых словах, возможно, просто синтаксический сахар, чтобы не запутывать разработчика.
Умур Контачи

1
@ fastreload, я полностью согласен с твоим комментарием .. Красиво написано!
PrimosK

@ fastreload ... Спасибо за чудесное объяснение. Если я правильно понял, наблюдатели для угловых выражений. Я прав?
Абилаш,

@PrimosK: добавляю вас для моего предыдущего комментария.
Абилаш,

2
Наблюдатели @Abilash предназначены для наблюдения за атрибутами, а не только за выражениями. Поэтому, если вы измените значение атрибута самостоятельно, оно будет отражено в следующем цикле дайджеста.
Умур Контачи

1

Я думаю, что это довольно очевидно:

  • $ Наблюдение используется в функции связывания директив.
  • $ watch используется в области видимости для отслеживания любых изменений его значений.

Имейте в виду : обе функции имеют два аргумента,

$observe/$watch(value : string, callback : function);
  • значение : всегда является строковой ссылкой на отслеживаемый элемент (имя переменной области действия или имя атрибута директивы, который будет отслеживаться)
  • обратный вызов : функция, которая будет выполнена в формеfunction (oldValue, newValue)

Я сделал plunker, так что вы можете понять, как их использование. Я использовал аналогию с Хамелеоном, чтобы сделать изображение проще.


2
Это довольно очевидно о его использовании. Но почему был вопрос. Марк подвел итог красиво.
Абилаш

3
Я думаю, что параметры могут быть изменены - кажется, что они передают значение newValue, а затем oldValue - attrs. $ Наблюдаем (). , ,
бластер

0

Почему $ наблюдение отличается от $ watch?

WatchExpression оценивается и сравнивается с предыдущим значением в каждом цикле digest (), если в значении watchExpression есть изменение, вызывается функция watch.

$ Наблюдение является специфическим для отслеживания интерполированных значений. Если значение атрибута директивы интерполировано, например,dir-attr="{{ scopeVar }}" , функция наблюдения будет вызываться только тогда, когда установлено интерполированное значение (и, следовательно, когда $ digest уже определил, необходимо выполнить обновления). По сути, уже есть наблюдатель для интерполяции, и функция $ наблюдайте за этим.

Смотрите $ наблюдаю и $ устанавливаем в compile.js

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.