AngularJS: Sự khác biệt giữa các phương thức $ obs và $ watch


378

Tôi biết rằng cả hai WatchersObserversđược tính toán ngay khi có gì đó $scopethay đổi trong AngularJS. Nhưng không thể hiểu chính xác sự khác biệt giữa hai là gì.

Sự hiểu biết ban đầu của tôi là Observersđược tính toán cho các biểu thức góc là các điều kiện ở phía HTML khi được Watchersthực thi khi $scope.$watch()hàm được thực thi. Tôi có suy nghĩ đúng không?


1
Chỉnh sửa của bạn không hữu ích và một chút đối kháng. Xin hãy quan tâm đến những người khác đến đây để được giúp đỡ thực sự.
độc thân

@ thợ thay đổi. Cảm ơn và xin lỗi!
Abilash

Không phải lo lắng. Cảm ơn đã sửa chữa.
độc thân

Câu trả lời:


608

$ obs () là một phương thức trênđối tượng Thuộc tính và do đó, nó chỉ có thể được sử dụng để quan sát / theo dõi sự thay đổi giá trị của thuộc tính DOM. Nó chỉ được sử dụng / được gọi bên trong các chỉ thị. Sử dụng $ obs khi bạn cần quan sát / xem thuộc tính DOM có chứa phép nội suy (nghĩa là {{}} 's).
Ví dụ,attr1="Name: {{name}}"sau đó trong một chỉ thị :attrs.$observe('attr1', ...).
(Nếu bạn thửscope.$watch(attrs.attr1, ...)nó sẽ không hoạt động vì {{}} s - bạn sẽ nhận đượcundefined.) Sử dụng $ watch cho mọi thứ khác.

$ watch () phức tạp hơn. Nó có thể quan sát / xem một "biểu thức", trong đó biểu thức có thể là hàm hoặc chuỗi. Nếu biểu thức là một chuỗi, nó là $ parse 'd (nghĩa là được đánh giá là biểu thức Angular ) thành một hàm. (Chính hàm này được gọi là mỗi chu kỳ phân loại.) Biểu thức chuỗi không thể chứa {{}} 's. $ watch là một phương thức trênđối tượng Phạm vi , vì vậy nó có thể được sử dụng / gọi bất cứ nơi nào bạn có quyền truy cập vào một đối tượng phạm vi, do đó trong

  • một bộ điều khiển - bất kỳ bộ điều khiển nào - một bộ điều khiển được tạo thông qua ng-view, ng-controller hoặc bộ điều khiển chỉ thị
  • một chức năng liên kết trong một lệnh, vì điều này cũng có quyền truy cập vào một phạm vi

Vì các chuỗi được đánh giá là biểu thức góc, $ watch thường được sử dụng khi bạn muốn quan sát / xem thuộc tính mô hình / phạm vi. Ví dụ, attr1="myModel.some_prop"sau đó trong một bộ điều khiển hoặc chức năng liên kết: scope.$watch('myModel.some_prop', ...)hoặc scope.$watch(attrs.attr1, ...)(hoặc scope.$watch(attrs['attr1'], ...)).
(Nếu bạn thử, attrs.$observe('attr1')bạn sẽ nhận được chuỗi myModel.some_prop, đó có thể không phải là điều bạn muốn.)

Như đã thảo luận trong các nhận xét về câu trả lời của @ PrimosK, tất cả các đồng hồ $ quan sát và $ được kiểm tra mỗi chu kỳ tiêu hóa .

Chỉ thị với phạm vi cách ly phức tạp hơn. Nếu cú ​​pháp '@' được sử dụng, bạn có thể $ obs hoặc $ xem thuộc tính DOM có chứa phép nội suy (nghĩa là {{}} '). (Lý do nó hoạt động với $ watch là vì cú pháp '@' thực hiện phép nội suy cho chúng ta, do đó $ watch nhìn thấy một chuỗi không có {{}} 's.) $ quan sát cho trường hợp này cũng.

Để giúp kiểm tra tất cả những điều này, tôi đã viết một Plunker xác định hai chỉ thị. Một ( d1) không tạo ra một phạm vi mới, còn lại ( d2) tạo ra một phạm vi cô lập. Mỗi chỉ thị có sáu thuộc tính giống nhau. Mỗi thuộc tính là cả $ obs'd và $ watch'ed.

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

Nhìn vào nhật ký giao diện điều khiển để thấy sự khác biệt giữa $ obs và $ watch trong chức năng liên kết. Sau đó nhấp vào liên kết và xem $ quan sát và đồng hồ $ nào được kích hoạt bởi các thay đổi thuộc tính được thực hiện bởi trình xử lý nhấp chuột.

Lưu ý rằng khi hàm liên kết chạy, mọi thuộc tính có chứa {{}} vẫn chưa được đánh giá (vì vậy nếu bạn cố kiểm tra các thuộc tính, bạn sẽ nhận được undefined). Cách duy nhất để xem các giá trị được nội suy là sử dụng $ obs (hoặc $ watch nếu sử dụng phạm vi cô lập với '@'). Do đó, nhận các giá trị của các thuộc tính này là một hoạt động không đồng bộ . (Và đây là lý do tại sao chúng ta cần các chức năng $ obs và $ watch.)

Đôi khi bạn không cần $ obs hoặc $ watch. Ví dụ: nếu thuộc tính của bạn chứa một số hoặc boolean (không phải là một chuỗi), chỉ cần đánh giá nó một lần : attr1="22", sau đó, giả sử, chức năng liên kết của bạn : var count = scope.$eval(attrs.attr1). Nếu nó chỉ là một chuỗi không đổi - attr1="my string"- thì chỉ cần sử dụng attrs.attr1trong lệnh của bạn (không cần $ eval ()).

Xem thêm bài viết trên nhóm google của Vojta về $ biểu thức đồng hồ.


13
Giải thích tuyệt vời! +1
PrimosK

4
Câu trả lời chính xác! Bạn có biết tại sao ng-src/ng-hrefsử dụng attr.$observethay vì scope.$watchsau đó?
okm

4
+1 cho Giáo hoàng AngularJS! Mỗi lần tôi tìm kiếm Stack cho một số thông tin về vấn đề Angular mới nhất của tôi, chắc chắn tôi sẽ đọc câu trả lời được chấp nhận @MarkRajcok.
GFoley83

1
Cảm ơn cho một bài viết tuyệt vời. phạm vi. $ eval (mục) thực sự hữu ích. Nếu mục là một chuỗi json, nó sẽ chuyển đổi thành một đối tượng json.
bnguyen82

5
@tamakisapes, chúng có thể hoán đổi cho nhau khi sử dụng @cú pháp. Tôi tin rằng không có sự khác biệt về hiệu suất (nhưng tôi đã không nhìn vào mã nguồn thực tế).
Mark Rajcok

25

Nếu tôi hiểu đúng câu hỏi của bạn, bạn đang hỏi sự khác biệt là gì nếu bạn đăng ký gọi lại với người nghe $watchhoặc nếu bạn làm điều đó với $observe.

Callback registerd với $watchđược kích hoạt khi $digestđược thực thi.

Gọi lại được đăng ký với $observeđược gọi khi thay đổi giá trị của các thuộc tính có chứa nội suy (ví dụ attr="{{notJetInterpolated}}").


Bên trong chỉ thị, bạn có thể sử dụng cả hai theo cách rất giống nhau:

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

hoặc là

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

3
Trên thực tế, vì mọi thay đổi được phản ánh theo $digestpha, nên có thể giả định rằng cuộc $observegọi lại sẽ được gọi $digest. Và $watchgọi lại cũng sẽ được gọi trong $digestnhưng bất cứ khi nào giá trị được thay đổi. Tôi nghĩ rằng họ làm cùng một công việc: "xem biểu thức, gọi lại cuộc gọi thay đổi giá trị". Sự khác biệt từ khóa có thể chỉ là đường cú pháp để không gây nhầm lẫn cho nhà phát triển.
Umur Kontacı

1
@fastreload, tôi hoàn toàn đồng ý với nhận xét của bạn .. Được viết một cách chính xác!
PrimosK

@fastreload ... Cảm ơn lời giải thích tuyệt vời. Nếu tôi hiểu chính xác, Người quan sát là dành cho Biểu thức góc. Tôi có đúng không
Abilash

@PrimosK: thêm bạn cho nhận xét trước của tôi.
Abilash

2
Các nhà quan sát @Abilash là để xem các thuộc tính dom, không chỉ các biểu thức. Vì vậy, nếu bạn tự thay đổi giá trị thuộc tính, nó sẽ được phản ánh trong chu trình phân loại tiếp theo.
Umur Kontacı

1

Tôi nghĩ rằng điều này là khá rõ ràng:

  • $ quan sát được sử dụng trong chức năng liên kết của các chỉ thị.
  • $ watch được sử dụng trên phạm vi để xem bất kỳ thay đổi nào trong giá trị của nó.

Hãy ghi nhớ : cả hai hàm có hai đối số,

$observe/$watch(value : string, callback : function);
  • value : luôn là một tham chiếu chuỗi đến thành phần được theo dõi (tên của biến phạm vi hoặc tên của thuộc tính của lệnh được theo dõi)
  • gọi lại : chức năng được thực hiện của biểu mẫufunction (oldValue, newValue)

Tôi đã thực hiện một plunker, vì vậy bạn thực sự có thể nắm bắt được cả việc sử dụng chúng. Tôi đã sử dụng sự tương tự Chameleon như để làm cho hình ảnh dễ dàng hơn.


2
Đó là khá rõ ràng về tập quán của nó. Nhưng tại sao là câu hỏi. Mark đã tóm tắt nó rất đẹp.
Abilash

3
Tôi nghĩ rằng các thông số có thể được chuyển đổi - nó xuất hiện để chuyển newValue, sau đó oldValue sang attrs. $ Obs (). . .
blaster

0

Tại sao $ quan sát khác với $ watch?

WatchExpression được đánh giá và so sánh với giá trị trước đó mỗi chu kỳ digest (), nếu có thay đổi giá trị watchExpression, chức năng đồng hồ được gọi.

$ quan sát cụ thể để theo dõi các giá trị nội suy. Nếu giá trị thuộc tính của lệnh được nội suy, ví dụ dir-attr="{{ scopeVar }}", hàm quan sát sẽ chỉ được gọi khi giá trị nội suy được đặt (và do đó khi $ digest đã xác định các cập nhật cần được thực hiện). Về cơ bản đã có người theo dõi nội suy và hàm cõng quan sát $ tắt.

Xem $ obs & $ được đặt trong compile.js

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.