在工做中,存在一個嵌套對象,須要展現嵌套對象內層的一些信息,因而寫了個Pipe
來處理,可是發現當嵌套的對象發生變化時,pipe
不會從新執行。例若有下面一個數據。javascript
var feer = { name: 'joe', skills: [ { name:'js' }, { name: 'ts' } ] }
咱們想要的結果是把skills
裏面的name
所有展現出來,以,
分割。css
// component export class AppComponent { private skills = ['css', 'html', 'java', 'gulp'] name = 'Angular 6'; feer = { name: 'joe', skills: [ { name: 'js' }, { name: 'ts' } ] } add() { const skill = this.skills.shift(); if (skill) { this.feer.skills.push({ name: skill }) } } } // html {{ feer.skills | defaultPure }} // Pipe import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'defaultPure' }) export class DefaultPurePipe implements PipeTransform { transform(feer:any): string { return feer.skills.map((v)=>v.name).join(','); } }
在這種狀況下,若是調用add
引發 skills
發生變化,pipe
不會從新計算,顯示的仍是初始值 js,ts
。html
這裏存在一個問題,對於上面👆例子中存在的嵌套結構,在 skills
變化的時候 ,transform
並無從新執行。這裏推測多是 內部檢測時候並不會作 deep-change-detection
(也就是不會關注 skills
裏的變化),最終致使了上面的問題: skills
發生了變化,但 pipe
並無從新執行計算變動輸出結果。vue
下面是摘自官網的一段話:java
Angular executes a pure pipe only when it detects a pure change to the input value. A pure change is either a change to a primitive input value (String
,Number
,Boolean
,Symbol
) or a changed object reference (Date
,Array
,Function
,Object
).Angular ignores changes within (composite) objects. It won't call a pure pipe if you change an input month, add to an input array, or update an input object property.git
pipe
忽略了 對象內部複合對象的變更(如例子中的 skills
), angular
不會作深度檢查,當咱們調用 add
方法時候,往 skills
數組裏 push
裏一個對象,對於 angular
來講, skills
是「未」發生變化的,由於引用是同樣的。github
在@Pipe
的 decorator 中除了name
還有一個叫 pure
類型爲 boolean
的 metadata
,官方解釋以下typescript
If Pipe is pure (its output depends only on its input.) Normally pipe'stransform
method is only invoked when the inputs to pipe'stransform
method change. If the pipe has internal state (it's result are dependant on state other than its arguments) than setpure
tofalse
so that the pipe is invoked on each change-detection even if the arguments to the pipe do not change.
大意是 pipe
僅在輸入發生改變的時候會再次執行 transform
方法。可是若是 pipe
有一個內部狀態,而且輸出依賴於這個內部狀態,那麼將 pure
設置爲 false
以便在每次 change-detection
(數據更新檢測) 時候執行 transform
即便輸入的數據並無改變。gulp
對於pure
, 默認值爲 true
,若是設置爲 false
,則能夠將上面的功能實現出來。可是同時要注意一個問題,一個 impure
的 pipe
(即 pure
= false ) 會常常被調用,若是有大量運算,則可能影響用戶體驗 。數組
一樣看一段官網的解釋:
Angular executes an impure pipe during every component change detection cycle. An impure pipe is called often, as often as every keystroke or mouse-move.
對於 impure
的 pipe
,angular
會在每次 component
的 變化檢測週期裏調用執行,甚至一次按鍵、一次鼠標事件都會觸發 pipe
的執行。
最終,對於上述的問題,只需修改 pipe
的代碼便可。
@Pipe({ name: 'defaultPure', pure: false }) export class DefaultPurePipe implements PipeTransform { transform(feer:any): string { return feer.skills.map((v)=>v.name).join(','); } }
對於上述問題,表面上來講加一下 pure
便可,對於深層次來講,涉及到了 angular
內部的一些工做原理。可是話說回來,目前僅僅研究到這種方案來處理,實際上應該還會有其餘的解決方案,大膽猜想能夠利用 change-detection
的一些鉤子來處理,待研究好了再記錄一下。
以前在遇到這個問題的時候,也是一臉懵逼的找答案,卻不知答案就在文檔上寫的清清楚楚。 🤷♀️~~~
BTW,其實在 vue
中也存在了一個相似的問題, 爲了性能等的考慮,不會對複合對象作深度變動檢測。而 Vue
的作法則簡單粗暴一些:從新裝飾數組方法。 具體能夠查看 Vue
的 源代碼 。