在工做中,存在一个嵌套对象,须要展现嵌套对象内层的一些信息,因而写了个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
的 源代码 。