3 回答

TA貢獻1811條經驗 獲得超6個贊
為了完全理解問題和可能的解決方案,我們需要討論角度變化檢測 - 用于管道和組件。
管道變化檢測
無狀態/純管
默認情況下,管道是無狀態/純粹的。無狀態/純管道只是將輸入數據轉換為輸出數據。他們不記得任何東西,所以他們沒有任何屬性 - 只是一種transform()
方法。因此,Angular可以優化無狀態/純管道的處理:如果它們的輸入沒有改變,則在變化檢測循環期間不需要執行管道。對于管道,如{{power | exponentialStrength: factor}}
,power
和factor
輸入。
對于這個問題,"#student of students | sortByName:queryElem.value"
,students
和queryElem.value
為輸入,并且管sortByName
是無狀態/純的。 students
是一個數組(引用)。
添加學生時,數組引用不會更改 -
students
不會更改 - 因此不會執行無狀態/純管道。當在過濾器輸入中輸入某些內容時,
queryElem.value
確實會更改,因此會執行無狀態/純管道。
解決陣列問題的一種方法是每次添加學生時更改數組引用 - 即,每次添加學生時創建新數組。我們可以這樣做concat()
:
this.students = this.students.concat([{name: studentName}]);
雖然這很有效,但我們的addNewStudent()
方法不應該僅僅因為我們使用管道而以某種方式實現。我們想用來push()
添加到我們的數組中。
有狀態的管道
有狀態管道具有狀態 - 它們通常具有屬性,而不僅僅是transform()
方法。即使輸入沒有改變,也可能需要對它們進行評估。當我們指定管道是有狀態/非純的時 - pure: false
- 每當Angular的更改檢測系統檢查組件的更改并且該組件使用有狀態管道時,它將檢查管道的輸出,無論其輸入是否已更改。
這聽起來像我們想要的,即使它效率較低,因為我們希望管道執行即使students
引用沒有改變。如果我們只是使管道有狀態,我們會收到一個錯誤:
EXCEPTION: Expression 'students | sortByName:queryElem.value in HelloWorld@7:6' has changed after it was checked. Previous value: '[object Object],[object Object]'. Current value: '[object Object],[object Object]' in [students | sortByName:queryElem.value
根據@ drewmoore的回答,“此錯誤僅在開發模式下發生(默認情況下從beta-0開始)。如果enableProdMode()
在引導應用程序時調用,則不會拋出錯誤?!?nbsp;國家文件ApplicationRef.tick()
:
在開發模式中,tick()還執行第二個更改檢測周期,以確保不會檢測到進一步的更改。如果在第二個周期中拾取了其他更改,則應用中的綁定會產生無法在單個更改檢測過程中解決的副作用。在這種情況下,Angular會拋出一個錯誤,因為Angular應用程序只能有一個更改檢測通道,在此期間必須完成所有更改檢測。
在我們的場景中,我認為錯誤是虛假/誤導。我們有一個有狀態的管道,每次調用時輸出都會改變 - 它可能有副作用,這沒關系。NgFor在管道之后進行評估,因此它應該可以正常工作。
但是,我們無法真正開發此錯誤,因此一種解決方法是將數組屬性(即狀態)添加到管道實現并始終返回該數組。請參閱@ pixelbits對此解決方案的回答。
但是,我們可以更高效,并且正如我們將看到的,我們將不需要管道實現中的數組屬性,并且我們不需要針對雙重更改檢測的變通方法。
組件變化檢測
默認情況下,在每個瀏覽器事件中,角度變化檢測都會遍歷每個組件以查看它是否發生了變化 - 輸入和模板(以及其他東西?)都會被檢查。
如果我們知道組件僅依賴于其輸入屬性(和模板事件),并且輸入屬性是不可變的,那么我們可以使用更有效的onPush
變更檢測策略。使用此策略,不會檢查每個瀏覽器事件,只有在輸入更改和模板事件觸發時才會檢查組件。而且,顯然,我們沒有Expression ... has changed after it was checked
通過此設置獲得該錯誤。這是因為在onPush
再次“標記”(ChangeDetectorRef.markForCheck()
)之前不會再次檢查組件。因此,模板綁定和有狀態管道輸出僅執行/評估一次。除非輸入改變,否則無狀態/純管道仍未執行。所以我們仍然需要一個有狀態的管道。
這是@EricMartinez建議的解決方案:帶有onPush
變化檢測的有狀態管道。請參閱@ caffinatedmonkey對此解決方案的回答。
請注意,使用此解決方案時,該transform()
方法不需要每次都返回相同的數組。我發現有點奇怪:沒有狀態的有狀態管道。再考慮一下......有狀態管道可能應該總是返回相同的數組。否則,它只能與onPush
開發模式下的組件一起使用。
畢竟,我認為我喜歡@ Eric和@ pixelbits的答案的組合:有狀態管道返回相同的數組引用,onPush
如果組件允許則進行更改檢測。由于有狀態管道返回相同的數組引用,因此管道仍可用于未配置的組件onPush
。
這可能會成為Angular 2的習慣用法:如果數組正在輸入管道,并且數組可能會更改(數組中的項目,而不是數組引用),我們需要使用有狀態管道。
- 3 回答
- 0 關注
- 876 瀏覽
添加回答
舉報