angular有像vue.js中的"计算属性"功能吗?

Pan*_*ano 26 javascript vue.js angular

我首先学习了Vue.js,现在在Angular 4中有一个项目,所以我刚刚学习了Angular.我发现除了"计算属性"之外,一切都与Vue没有什么不同.在Vue中,我可以创建一个计算属性来侦听其他属性的更改并自动运行计算.

例如(在Vue 2中):

computed: {
    name(){
        return this.firstname + ' ' + this.lastname;
    }
}
Run Code Online (Sandbox Code Playgroud)

name属性只会在firstname或lastname之一更改时重新计算.如何在Angular 2或4中处理此问题?

And*_*vic 41

虽然这已经得到了回答,但我认为这不是一个很好的答案,用户不应该使用getter作为角度计算属性.为什么你会问?getter只是函数的糖语法,它将被编译为普通函数,这意味着它将在每次更改检测检查时执行.这对于表现来说很糟糕,因为在任何变化时都会重新计算数百次财产.

看一下这个例子:https://plnkr.co/edit/TQMQFb?p = preview

@Component({
    selector: 'cities-page',
    template: `
        <label>Angular computed properties are bad</label>

        <ng-select [items]="cities"
                   bindLabel="name"
                   bindValue="id"
                   placeholder="Select city"
                   [(ngModel)]="selectedCityId">
        </ng-select>
        <p *ngIf="hasSelectedCity">
            Selected city ID: {{selectedCityId}}
        </p>
        <p><b>hasSelectedCity</b> is recomputed <b [ngStyle]="{'font-size': calls + 'px'}">{{calls}}</b> times</p>
    `
})
export class CitiesPageComponent {
    cities: NgOption[] = [
        {id: 1, name: 'Vilnius'},
        {id: 2, name: 'Kaunas'},
        {id: 3, name: 'Pabrad?'}
    ];
    selectedCityId: any;

    calls = 0;

    get hasSelectedCity() {
      console.log('hasSelectedCity is called', this.calls);
      this.calls++;
      return !!this.selectedCityId;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你真的想要计算属性,你可以使用像mobx这样的状态容器

class TodoList {
    @observable todos = [];
    @computed get unfinishedTodoCount() {
        return this.todos.filter(todo => !todo.finished).length;
    }
}
Run Code Online (Sandbox Code Playgroud)

mobx具有@computed装饰器,因此只有在需要时才会缓存并重新计算getter属性

  • 这应该是可接受的答案,因为这是原始答案不模仿VueJS计算的属性的唯一指向。谢谢。 (7认同)
  • 感谢您指出为此使用 get 属性对性能的影响。真正有用的信息。 (2认同)

Ale*_*xei 15

我会尽力改进,Andzej Maciusovic希望得到一个规范的答案.实际上,VueJS有一个叫做计算属性的功能,可以使用一个例子快速显示:

<template>
  <div>
    <p>A = <input type="number" v-model="a"/></p>
    <p>B = <input type="number" v-model="b"/></p>
    <p>C = <input type="number" v-model="c"/></p>
    <p>Computed property result: {{ product }}</p>
    <p>Function result: {{ productFunc() }}</p>
  </div>
</template>

<script>
export default {
  data () {
    return {
      a: 2,
      b: 3,
      c: 4
    }
  },

  computed: {
    product: function() {
      console.log("Product called!");
      return this.a * this.b;
    }
  },

  methods: {
    productFunc: function() {
      console.log("ProductFunc called!");
      return this.a * this.b;
    }
  }
}
</script>
Run Code Online (Sandbox Code Playgroud)

每当用户更改输入值ab,两者productproductFunc正在登录到控制台.如果用户更改c,则仅productFunc调用.

回到Angular,mobxjs真的有助于解决这个问题:

  1. 使用安装它 npm install --save mobx-angular mobx
  2. 绑定属性的用法observablecomputed属性

TS文件

    import { observable, computed } from 'mobx-angular';

    @Component({
       selector: 'home',
       templateUrl: './home.component.html',
       animations: [slideInDownAnimation]
    })
    export class HomeComponent extends GenericAnimationContainer {
       @observable a: number = 2;
       @observable b: number = 3;
       @observable c: number = 4;

       getAB = () => {
           console.log("getAB called");
           return this.a * this.b;
       }

       @computed get AB() {
           console.log("AB called");
           return this.a * this.b;
       }
    }
Run Code Online (Sandbox Code Playgroud)

标记

<div *mobxAutorun>
    <p>A = <input type="number" [(ngModel)]="a" /> </p>
    <p>B = <input type="number" [(ngModel)]="b" /> </p>
    <p>C = <input type="number" [(ngModel)]="c" /> </p>
    <p> A * B = {{ getAB() }}</p>
    <p> A * B (get) = {{ AB }}</p>
</div>
Run Code Online (Sandbox Code Playgroud)

如果ab更改,AB则调用一次和getAB多次.如果c更改,则仅getAB调用.因此,即使必须执行计算,此解决方案也更有效.


Jul*_*ova 9

是的你可以.

在TS文件中:

export class MyComponent {

  get name() {
     return  this.firstname + ' ' + this.lastname;
  }
}
Run Code Online (Sandbox Code Playgroud)

之后在html中:

<div>{{name}}</div>
Run Code Online (Sandbox Code Playgroud)

这是一个例子:

@Component({
  selector: 'my-app',
  template: `{{name}}`,
})
export class App  {
  i = 0;
  firstN;
  secondN;

  constructor() {
    setInterval(()=> {
      this.firstN = this.i++;
      this.secondN = this.i++;
    }, 2000);
  }
  get name() {
    return  this.firstN + ' ' + this.secondN;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这不是计算属性:只需将`console.log`放在此get函数的开头,然后放入几个重复的`{{name}}`表达式,您将看到为每个额外的`调用该函数` {{name}}`来电.计算属性的重点是仅计算一次(每次更改基础值),并多次使用它. (36认同)
  • 为什么?name将在视图中更新,然后firstName或secondName被更改.这不是vue.js发明.Knockout.js已于7年前计算过道具. (3认同)
  • 不,这不是OP所指的。 (2认同)
  • VueJS计算属性功能在更改检测方面比此功能更聪明,它仅在基础属性发生更改时才评估功能。 (2认同)
  • Aurelia的也是。:) https://aurelia.io/docs/binding/computed-properties#introduction (2认同)
  • 我通常和 Aurelia 一起工作,我正在帮助一个 Angular 项目。我非常怀疑这不是 Angular 提供的东西,此外,这里的大多数答案都推荐状态管理库只是为了做基于更改的属性。这太疯狂了。 (2认同)

rmi*_*ero 7

computed Vue 中的属性有两个巨大的好处:反应性和记忆性。

在 Vue 中,我可以创建一个计算属性来监听其他属性的变化并自动运行计算。

您在这里专门询问 Angular 中的反应性系统。似乎人们已经忘记了什么是 Angular 的基石:Observables。

const { 
    BehaviorSubject, 
    operators: {
      withLatestFrom,
      map
    }
} = rxjs;
const firstName$ = new BehaviorSubject('Larry');
const lastName$ = new BehaviorSubject('Wachowski');

const fullName$ = firstName$.pipe(
    withLatestFrom(lastName$),
    map(([firstName, lastName]) => `Fullname: ${firstName} ${lastName}`)
);

const subscription = fullName$.subscribe((fullName) => console.log(fullName))

setTimeout(() => {
    firstName$.next('Lana');
    subscription.unsubscribe();
}, 2000);
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.js"></script>
Run Code Online (Sandbox Code Playgroud)

我的代码没有解决记忆部分,它只解决反应性。

人们给出了一些有趣的答案。虽然mobX computed属性可能(我从未使用过 mobX)更接近computedVue 中的属性,但这也意味着您正在依赖另一个您可能不感兴趣使用的库。对于您解释的特定用例,管道替代方案在这里最有趣,因为它解决了反应性和记忆化问题,但并非对所有情况都有效。


key*_*eom 5

在某些情况下,使用“纯管道”可能是一个合理的选择,显然带有一些限制,但至少避免了在任何事件上执行该函数的成本。

@Pipe({ name: 'join' })
export class JoinPipe implements PipeTransform {
  transform(separator: string, ...strings: string[]) {
    return strings.join(separator);
  }
}
Run Code Online (Sandbox Code Playgroud)

在模板中,您可以使用而不是全名属性' ' | join:firstname:lastname。很遗憾,角度的计算属性仍然不存在。