3 回答

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