核心要點
$watch
函數是觀察變量值或表達式變化的強大工具。檢測到變化時,它會觸發一個回調函數,該函數會在每次被監視的變量發生變化時執行。 $watch
使用JavaScript的相等運算符(===)進行比較。如果新值與舊值不同,則觸發回調函數。但是,需要注意的是,默認情況下,$watch
只檢查引用相等性,這意味著只有當為被監視的變量分配新值時,才會觸發回調函數。 $watchGroup
和$watchCollection
作為方便的快捷方式,分別用於設置具有相同回調函數的多個監視器或監視數組或對象。但是,這些方法只執行淺監視,只對引用更改做出反應。 $watch
,尤其是在多個變量上使用,可能會影響性能,因為需要在每個摘要周期中檢查所有被監視變量的變化。開發人員應根據情況考慮使用$watchGroup
或$watchCollection
,或限制被監視變量的數量以提高性能。 本文由Mark Brown同行評審。感謝所有SitePoint的同行評審員,使SitePoint的內容達到最佳狀態!
AngularJS提供了許多不同的選項,可以通過三種不同的“watch”方法使用發布-訂閱模式。每種方法都採用可選參數來修改其行為。
關於$watch
的官方文檔遠非詳盡:畢竟,這是一個困擾AngularJS v1整體的問題。即使是解釋如何進行的在線資源,充其量也是零散的。
因此,最終,開發人員很難為特定情況選擇正確的方法。對於AngularJS初學者來說尤其如此!結果可能會令人驚訝或不可預測,這不可避免地會導致錯誤。
在本文中,我將假設您熟悉AngularJS概念。如果您覺得需要復習,您可能需要閱讀有關$scope
、綁定以及$apply
和$digest
的內容。
檢查您的理解
例如,監視數組的第一個元素的最佳方法是什麼?假設我們在我們的作用域上聲明了一個數組,$scope.letters = ['A','B','C']
;
$scope.$watch('letters', function () {...});
是否會觸發其回調函數? $scope.$watch('letters[0]', function () {...});
呢?它會以相同的方式工作,還是更好? $watch
、$watchCollection
和$watchGroup
之間有什麼區別? 如果您對所有這些問題感到困惑,請繼續閱讀。我的目標是通過幾個示例盡可能清楚地說明這一點,從而引導您完成整個過程。
$scope.$watch
讓我們從$scope.$watch
開始。這是所有watch功能的核心:我們將看到的每種其他方法都只是$watch
的便捷快捷方式。
$watch
現在,Angular的優點是您可以顯式地使用相同的機制在控制器中執行由數據更改觸發的複雜操作。例如,您可以對某些數據設置監視器,這些數據可以響應以下內容而更改:
您可以只設置一個偵聽器來處理任何數據更改,無論是什麼原因造成的。
但是,要這樣做,您需要自己調用$scope.$watch
。
讓我們看看$rootscope.watch()
的代碼。
這是它的簽名:function(watchExp, listener, objectEquality, prettyPrintExpression)
。
詳細來說,它的四個參數:
watchExp
被監視的表達式。它可以是函數或字符串,它在每個摘要周期中都會被評估。
這裡需要注意的一個關鍵方面是,如果表達式被評估為函數,則該函數需要是冪等的。換句話說,對於相同的輸入集,它應該始終返回相同的輸出。如果不是這種情況,Angular將假設被監視的數據已更改。反過來,這意味著它將繼續檢測差異並在摘要周期的每次迭代中調用偵聽器。
listener
一個回調函數,在首次設置監視器時觸發,然後在摘要周期中每次檢測到watchExp
值的更改時觸發。設置時的初始調用旨在為表達式存儲初始值。
objectEquality
當且僅當此值為true時,監視器將執行深度比較。否則,它執行淺比較,即僅比較引用。
讓我們以數組為例:$scope.fruit = ["banana", "apple"]
;
objectEquality == false
意味著只有重新分配fruit
字段才會導致調用偵聽器。
我們還需要檢查“深度”比較有多深:我們稍後會討論這一點。
prettyPrintExpression
如果傳遞,它將覆蓋監視表達式。此參數並非旨在在對$watch()
的正常調用中使用;它由表達式解析器內部使用。
小心:正如您自己所看到的,當意外傳遞第四個參數時,很容易出現意外的結果。
現在我們準備回答引言中的一些問題。請查看本節的示例:
請隨意熟悉它們;您可以直接比較行為差異,或按照文章中的順序進行。
因此,您需要監視作用域上的數組以進行更改,但是“更改”是什麼意思?
假設您的控制器看起來像這樣:
<code class="language-javascript">app.controller('watchDemoCtrl', ['$scope', function($scope){ $scope.letters = ['A','B','C']; }]);</code>
一種選擇是使用這樣的調用:
<code class="language-javascript">$scope.$watch('letters', function (newValue, oldValue, scope) { // 对 $scope.letters 执行任何操作 });</code>
在上面的回調中,newValue
和oldValue
具有不言自明的含義,並且每次$digest
週期調用它時都會更新。 scope
的含義也很直觀,因為它保存對當前作用域的引用。
但是,關鍵是:何時會調用此偵聽器?事實上,您可以添加、刪除、替換letters
數組中的元素,而不會發生任何事情。這是因為,默認情況下,$watch
假定您只想要引用相等性,因此只有當您為$scope.letters
分配新值時,才會觸發回調函數。
如果您需要對數組的任何元素的更改採取行動,則需要將true
作為第三個參數傳遞給watch
(即作為上面描述的可選objectEquality
參數的值)。
<code class="language-javascript">$scope.$watch('letters', function (newValue, oldValue, scope) { // 对 $scope.letters 执行任何操作 }, true);</code>
對於對象,情況沒有改變:如果objectEquality
為false,您只需監視對該作用域變量的任何重新賦值,而如果為true,則每次更改對像中的元素時都會觸發回調函數。
值得注意的是,通過使用objectEquality === true
監視數組,每次觸發回調函數時,newValue
和oldValue
將是整個數組的新舊值。因此,您必須將它們彼此進行比較才能了解實際發生了什麼變化。
假設您只對數組中第一個元素(或第四個元素——原理相同)的更改感興趣。由於Angular非常出色,它允許您這樣做:您可以在作為第一個參數傳遞給$watch
的表達式中以自然的方式表達它:
<code class="language-javascript">$scope.$watch('letters[4]', function (newValue, oldValue, scope) { //... }, true);</code>
如果數組只有2個元素會怎樣?沒問題,除非您添加第四個元素,否則您的回調函數不會被觸發。好吧,好的,從技術上講,它會在您設置監視器時觸發,然後只有在您添加第四個元素時才會觸發。
如果您記錄oldValue
,您會看到在這兩種情況下它都將是未定義的。將此與監視現有元素時發生的情況進行比較:在設置時,您仍然有oldValue == undefined
。所以$watch
無法處理!
現在一個更有趣的問題:我們在這裡需要傳遞objectEquality === true
嗎?
簡短的回答:對不起,沒有簡短的回答。
這確實取決於:
objectEquality
。 $scope.board = [[1, 2, 3], [4, 5, 6]]
;,並且我們想監視第一行。然後我們可能希望在像$scope.board[0][1] = 7
這樣的賦值更改它時收到警報。 也許比監視數組中的任意元素更有用的是,我們可以監視對像中的任意字段。但這並不奇怪,對吧?畢竟,JavaScript中的數組是對象。
<code class="language-javascript">app.controller('watchDemoCtrl', ['$scope', function($scope){ $scope.letters = ['A','B','C']; }]);</code>
在這一點上,我們還需要闡明最後一個但至關重要的細節:如果我們需要監視一個複雜的嵌套對象,其中每個字段都是非原始值,會發生什麼?例如樹或圖,或者只是一些JSON數據。
讓我們檢查一下!
首先,我們需要一個要監視的對象:
<code class="language-javascript">$scope.$watch('letters', function (newValue, oldValue, scope) { // 对 $scope.letters 执行任何操作 });</code>
讓我們為整個對象設置監視器:我認為,到目前為止,很清楚的是,在這種情況下必須將objectEquality
設置為true
。
<code class="language-javascript">$scope.$watch('letters', function (newValue, oldValue, scope) { // 对 $scope.letters 执行任何操作 }, true);</code>
問題是:如果像$scope.b.bb[1].bb2a = 7;
這樣的賦值發生,Angular是否會足夠好心地讓我們知道?
答案是:是的,幸運的是,它會(在之前的CodePen演示中查看)。
其他方法
$scope.$watchGroup
$watchGroup()
真的是一種不同的方法嗎?答案是否定的,它不是。
$watchGroup()
是一個方便的快捷方式,允許您使用相同的回調函數設置多個監視器,並傳遞一個watchExpressions
數組。
每個傳遞的表達式都將使用標準$scope.$watch()
方法進行監視。
<code class="language-javascript">$scope.$watch('letters[4]', function (newValue, oldValue, scope) { //... }, true);</code>
值得注意的是,使用$watchGroup
,newValues
和oldValues
將保存表達式的值列表,包括發生更改的值和保持相同值的那些值,順序與它們在第一個參數的數組中傳遞的順序相同。
如果您檢查了此方法的文檔,您可能會注意到它沒有採用objectEquality
選項。這是因為它淺監視表達式,並且只對引用更改做出反應。
如果您使用下面的$watchGroup()
演示進行操作,您可能會對一些細微之處感到驚訝。例如,unshift
將導致調用偵聽器,至少在某種程度上是這樣:這是因為當將表達式列表傳遞給$watchGroup
時,任何一個表達式觸發都會導致執行回調函數。
此外,請注意,$scope.obj.b
的任何子字段的任何更改都不會產生任何更新——只有為b字段本身分配新值才會產生更新。
$scope.$watchCollection
這是監視數組或對象的另一個便捷快捷方式。對於數組,當替換、刪除或添加任何元素時,將調用偵聽器。對於對象,當更改任何屬性時。同樣,$watchCollection()
不允許objectEquality
,因此它只會淺監視元素/字段,並且不會對它們的子字段的更改做出反應。
結論
希望這些示例能幫助您發現此Angular功能的強大功能,並了解使用正確的選項有多麼重要。
隨意複製CodePen並嘗試在不同的上下文中使用這些方法,並且不要忘記在評論區留下您的反饋!
如果您想更深入地了解我們在本文中討論的一些概念,以下是一些進一步閱讀的建議:
$apply()
和$digest()
$watch
等的文檔關於在AngularJS中掌握$watch
的常見問題解答 (FAQ)
$watch
在AngularJS中的主要目的是什麼? AngularJS中的$watch
函數主要用於觀察變量或表達式的值的變化。它是AngularJS作用域對象的一部分,用於監視變量或表達式的值的變化。檢測到變化時,$watch
函數會觸發一個回調函數,該函數會在每次被監視的變量發生變化時執行。
$watch
在AngularJS中是如何工作的? AngularJS中的$watch
函數通過比較被監視變量或表達式的舊值和新值來工作。它使用JavaScript的相等運算符(===)進行比較。如果新值與舊值不同,$watch
函數將觸發回調函數。
$watch
? 要在AngularJS中使用$watch
,您需要在作用域對像上調用$watch
方法,並向其傳遞兩個參數:要監視的變量或表達式的名稱,以及在被監視的變量發生變化時要執行的回調函數。這是一個示例:
<code class="language-javascript">app.controller('watchDemoCtrl', ['$scope', function($scope){ $scope.letters = ['A','B','C']; }]);</code>
$watch
和$apply
在AngularJS中的區別是什麼? AngularJS中的$watch
函數用於觀察變量或表達式的變化,而$apply
函數用於手動啟動AngularJS摘要周期,該週期會檢查被監視變量的任何變化並相應地更新視圖。 $apply
函數通常在AngularJS上下文之外進行模型更改時使用,例如在DOM事件處理程序或setTimeout
函數中。
$watch
在AngularJS中監視多個變量嗎? 是的,您可以使用$watch
在AngularJS中監視多個變量。您可以通過將變量名稱數組傳遞給$watch
函數來實現此目的。但是,請記住,監視多個變量可能會影響性能,因為$watch
函數需要在每個摘要周期中檢查所有被監視變量的變化。
$watch
中的變量? 當您在AngularJS中調用$watch
函數時,它會返回一個註銷函數。您可以調用此函數來停止監視變量。這是一個示例:
<code class="language-javascript">$scope.$watch('letters', function (newValue, oldValue, scope) { // 对 $scope.letters 执行任何操作 });</code>
$watchGroup
是什麼? AngularJS中的$watchGroup
函數用於監視一組表達式。它的工作方式類似於$watch
函數,但它只在每個摘要周期觸發一次回調函數,即使多個被監視的表達式發生了變化也是如此。這可以在監視多個表達式時提高性能。
$watchCollection
是什麼? AngularJS中的$watchCollection
函數用於監視對象的屬性或數組的元素。只要任何屬性或元素發生變化,它就會觸發回調函數,但與$watch
不同,它不會深度監視對像或數組,這可以提高性能。
$watch
嗎? 是的,您可以在AngularJS指令中使用$watch
。事實上,在指令中使用$watch
來響應指令的屬性或作用域變量的變化是一種常見的做法。
$watch
在AngularJS中有哪些性能方面的考慮? 使用$watch
在AngularJS中可能會影響性能,尤其是在監視許多變量或表達式時。這是因為$watch
函數需要在每個摘要周期中檢查所有被監視變量的變化。為了提高性能,請根據情況考慮使用$watchGroup
或$watchCollection
,或限制被監視變量的數量。
以上是掌握$ Watch在Angularjs的詳細內容。更多資訊請關注PHP中文網其他相關文章!