Angular中的纯粹和不纯的管道之间的本质区别以及为什么这很重要

前端之家收集整理的这篇文章主要介绍了Angular中的纯粹和不纯的管道之间的本质区别以及为什么这很重要前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

在Angular中编写自定义管道时,可以指定是定义纯管道还是不纯管道:

@Pipe({
  name: 'myCustomPipe',pure: false/true        <----- here (default is `true`)
})
export class MyCustomPipe {}

Angular在管道上有一个非常好的文档,你可以在这里找到。 但是,正如文件经常发生的情况一样,没有明确的划分理由。 在这篇文章中,我想填补这个空白,并展示了函数式编程的前景,它展示了纯和不纯的管道来自何处。 除了学习差异,你会知道它是如何影响性能,这些知识将帮助你编写高效和高性能的管道。

A pure function

网上有很多关于函数式编程的信息,可能每个开发人员都知道纯函数是什么。 对于我自己,我将一个纯函数定义为一个没有内部状态的函数。 这意味着它执行的所有操作都不受状态的影响,给出相同的输入参数产生相同的确定性输出

以下是添加数字的两个版本的函数。 第一个是纯的,第二个是不纯的:

const addPure = (v1,v2) => {
  return v1 + v2;
};

const addImpure = (() => {
  let state = 0;
  return (v) => {
    return state += v;
  }
})();

如果我用相同的输入调用两个函数,比如说数字1,则第一个函数在每次调用时都会产生相同的输出2:

addPure(1,1);  // 2
addPure(1,1);  // 2

而第二个产生不同的输出

addImpure(1);  // 1
addImpure(1);  // 2
addImpure(1);  // 3

所以这里的关键是即使输入不变,不纯的函数也会产生不同的输出。 这意味着我们不能使用输入值来确定输出是否会改变。

函数

  • 输入参数值决定了输出,所以如果输入参数不改变,输出不会改变
  • 可以多次调用,而不会影响输出结果

非纯函数

  • 不能使用输入值来确定输出是否会改变
  • 不能共用,因为内部状态会受到外界的影响

Applying that knowledge to Angular pipes

假设我们定义了一个自定义管道,纯管道:

@Pipe({
  name: 'myCustomPipe',pure: true
})
export class MyCustomPipe {}

在组件模板中像这样使用它:

<span>{{v1 | customPipe}}</span>
<span>{{v2 | customPipe}}</span>

因为管道是纯的,这意味着没有内部状态,管道可以被共享。Angular是怎样实现的?尽管模板中有,但它可以只创建一个管道实例,该实例可以在用法之间共享。
对于那些从我以前的文章中了解到组件工厂的人来说,这里有一个相关的编译代码,它只定义了一个管道定义:

function View_AppComponent_0(_l) {
  return viewDef_1(0,[
    pipeDef_2(0,ExponentialStrengthPipe_3,[]),// node index 0
    ...

这是在updateRenderer函数中共享的:

function(_ck,_v) {
    unwrapValue_7(_v,4,_ck(_v,5,nodeValue_8(_v,0),...);
                                                   ^^^
    unwrapValue_7(_v,8,9,...);
                                                   ^^^

这里是unwrapValue函数用于通过调用transform来检索当前的管道值。 管道实例由nodeValue函数调用中的节点索引引用 - 在这种情况下为0。

但是,如果我们将管道定义为不纯,假设有一些内部状态:

@Pipe({
  name: 'myCustomPipe',pure: false
})
export class MyCustomPipe {}

我们不希望第二次使用中的管道在第一次使用时受到其调用的影响,所以Angular会创建两个管道实例,每个实例都有自己的状态:

function View_AppComponent_0(_l) {
   return viewDef(0,[
       ...
       pipeDef_2(0,ExponentialStrengthPipe,[]) // node index 4
       ...
       pipeDef_2(0,[]) // node index 8

并且它在updateRenderer函数中不共享:

function(_ck,4),8),...);
                                                   ^^^

你可以在这里看到,对于每个用法Angular使用不同的节点索引 - 4和8来代替节点索引0。

第一章总结的第二点是,用纯函数我们可以用输入值来确定输出是否会变化,而不纯的函数则不能保证。

在Angular中,我们将输入参数传递给管道,如下所示:

<span>{{v1 | customPipe:param1:param2}}</span>

所以如果一个管道是纯的,我们知道它的输出(通过变换方法)是由输入参数严格确定的。 如果输入参数不改变,输出不会改变。 这个推理允许Angular只有在输入参数改变时才优化管道和调用变换方法

但是,如果管道不纯并且具有内部状态,则相同的参数不能保证相同的输出。 这意味着Angular被迫在每个摘要上的管道实例上触发转换函数

不纯管道的一个很好的例子是来自@ angular / common包的AsyncPipe。 此管道具有内部状态,该状态持有通过订阅传递给管道的observable作为参数创建的基础订阅。 由于Angular必须为每个管道使用创建一个新实例,以防止不同的可观察对象相互影响。 而且每个摘要上还必须调用变换方法,因为甚至认为可观察参数可能不会改变新的值,可能通过这个需要被变换检测处理的可观察到。

另外两个不纯的管子是JsonPipe和SlicePipe。 Angular对管道施加了额外的限制,被认为是纯粹的 - 管道的输入是不可变的。 如果输入是可变的,则需要在每个摘要上重新评估管道,因为可以在不更改对象引用(管道参数保持不变)的情况下改变输入对象。 这就是为什么尽管没有内部状态,JsonPipe和SlicePipe管道都不被认为是纯粹的。

其余的Angular默认管道是纯的。

Conclusion

所以,正如我们所看到的,如果不明智和谨慎使用不纯的管道,可能会有明显的性能下降。 性能受到影响的原因是Angular创建了不纯的管道的多个实例,并且在每个摘要循环中将其称为变换方法

我希望通过阅读文章,您现在知道两种类型之间的区别,在设计和实现自定义管道时,Angular如何处理这两种类型,以及您应该使用哪种心智模型。

猜你在找的Angularjs相关文章