当前位置: 首页 > news >正文

【译】Angular自动取消订阅

  • 原文链接:https://netbasal.com/automagically-unsubscribe-in-angular-4487e9853a88
  • 创建时间:2018-06-18
  • 修改时间:2018-06-20
  • 参与人员:@Zaynex,@我爱吃南瓜

如你所知,当你在 Javascript 中订阅一个 observable 或者事件时,你通常需要在特定的时候取消订阅以释放内存。否则,就会导致内存泄漏

A memory leak occurs when a section of memory that is no longer being used is still being occupied needlessly instead of being returned to the OS

当一部分内存不再使用但它仍被不必要得占用而不是返回给操作系统时就会产生内存泄漏。

在 Angular 的组件或者指令中,你需要在 ngOnDestroy 的生命周期中 unsubscribe。

比如,如果你有一个组件,它有2个订阅源。

@Component({
  selector: 'test',
  template: `...`,
})
export class TestComponent {
  one$;
  two$;

  constructor( private store: Store<any>, private element : ElementRef ) {}

  ngOnInit() {
    this.one$ = store.select("data").subscribe(data => // do something);
    this.two$ = Observable.interval(1000).subscribe(data => // do something);
  }

  ngOnDestroy() {
   this.one$.unsubscribe();
   this.two$.unsubscribe();
  }

}
复制代码

你需要创建 ngOnDestroy 的方法并且给每个订阅源取消订阅。

这很不错,但是我想要这些取消订阅的过程自动化。如果我可以创建一个 decorator 类去帮我做这个事情会怎样呢? 我们假设它会像下面这样:

@Component({
  selector: 'test',
  template: `...`,
})
@AutoUnsubscribe
export class TestComponent {
  one$;
  two$
  three;

  constructor( private store: Store<any>, private element : ElementRef) {}

  ngOnInit() {
    //...same subscriptions
  }

  // Notice that we don't have the ngOnDestroy method anymore
}
复制代码

让我们创建一个类装饰器并且给它命名为 AutoUnsubscribe

TypescriptBabel 中, 类装饰器仅仅是一个接受一个参数的、被装饰的类的构造函数。 (a class decorator is just a function that takes one parameter, the constructor of the decorated class.)

类装饰器作用于类的 constructor,并且观察、修改或者替换一个类的定义。

export function AutoUnsubscribe( constructor ) {

  const original = constructor.prototype.ngOnDestroy;

  constructor.prototype.ngOnDestroy = function () {
    for ( let prop in this ) {
      const property = this[ prop ];
      if ( property && (typeof property.unsubscribe === "function") ) {
        property.unsubscribe();
      }
    }
    original && typeof original === "function" && original.apply(this, arguments);
  };
}
复制代码

? 这里于三个简单的步骤:

  1. 保存一个引用指向原来的 ngOnDestory 函数。
  2. 创建你自己的 ngOnDestroy,循环遍历类的属性并且调用 unsubscribe 函数,如果存在的话。
  3. 调用原来的 ngOndestroy 函数

? 但是... 等等,如果因为某些疯狂的理由,你仍需要保留订阅源呢?比如当组件注销的时候你并不想取消订阅 $two 这个订阅源。

这种情况下我们需要传入一个参数给装饰器(一个数组用来过滤自动订阅),所以我们需要使用Decorator Factory

A Decorator Factory is simply a function that returns the expression that will be called by the decorator at runtime. 一个装饰器工厂就是一个函数,它会返回的运行期间被装饰器调用的函数。

export function AutoUnsubscribe(blackList = []) {
  return function(constructor) {
    const original = constructor.prototype.ngOndestroy;

    constructor.prototype.ngOndestroy = function() {
      for(let prop in this) {
        const property = this[prop];
        if(!blackList.includes(prop)) {
          if(property && (typeof property.unsubscribe === 'function')) {
            property.unsubscribe();
          }
        }
      }

      original && typeof original === 'function' && origin.apply(this, arguments);
    };
  }
}
复制代码

我们只是针对属性名做了检测,如果它不在 blacklist 的名单里那我们就调用 unsubscribe()

现在我们可以像这样使用 decorator:

@AutoUnsubscribe(["one$", "two$"])
class TestComponent {
  ...
}
复制代码

? 现在我们完成了! 你可以在这里找到decorator,如果你有更好的想法,欢迎 pull request。

如果你想要更加声明式的方式在takeUntil 中使用,你可以查看我的 tiny class decorator,它有能力去做到:

import TakeUntilDestroy from "angular2-take-until-destroy";

@Component({
  selector: 'app-inbox',
  templateUrl: './inbox.component.html'
})
@TakeUntilDestroy
export class InboxComponent {
  componentDestroy;
  constructor( ) {
    const timer$ = Observable.interval(1000)
      .takeUntil(this.componentDestroy())
      .subscribe(val => console.log(val))

    const timer2$ = Observable.interval(2000)
      .takeUntil(this.componentDestroy())
      .subscribe(val => console.log(val))
  }
}
复制代码

不要害怕看源码,它就那么几行! 如果你喜欢这篇文档,查看我之前的一篇————Make your Code Cleaner with Decorators

总结

你可以利用装饰器为你的应用添加强大的功能。 装饰器不是一个库也不是框架,所以要善于创造并利用好他们。 你可以探索更多的 decorators,在这里

相关文章:

  • Blog-05-《一周快速上手Kotlin For Android》-之Broadcast用法
  • pandas 打开没有列名的表格,并命名
  • KVOController代码分析和踩坑
  • Shell命令 中|| 使用
  • 针对ASP.NET Core Web API的先进架构
  • plsql 表中字段及注释时为乱码
  • BAT脚本编写教程入门提高篇
  • 时间序列异常检测算法S-H-ESD
  • docker实战
  • Hive 部署
  • Openstack 之 Prometheus 监控
  • python3 异步模块asyncio
  • 紫书 习题 11-10 UVa 12264 (二分答案+最大流)
  • 大数据经典学习路线(及供参考)
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • DataBase in Android
  • Hexo+码云+git快速搭建免费的静态Blog
  • JavaScript创建对象的四种方式
  • Vue ES6 Jade Scss Webpack Gulp
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 前嗅ForeSpider采集配置界面介绍
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 网络应用优化——时延与带宽
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • 我的zsh配置, 2019最新方案
  • 以太坊客户端Geth命令参数详解
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​业务双活的数据切换思路设计(下)
  • #pragma once与条件编译
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (三)Honghu Cloud云架构一定时调度平台
  • (算法)前K大的和
  • (学习日记)2024.03.25:UCOSIII第二十二节:系统启动流程详解
  • (转) ns2/nam与nam实现相关的文件
  • (转)重识new
  • .Mobi域名介绍
  • .NET 常见的偏门问题
  • .net反混淆脱壳工具de4dot的使用
  • .NET精简框架的“无法找到资源程序集”异常释疑
  • .pop ----remove 删除
  • /bin、/sbin、/usr/bin、/usr/sbin
  • ::
  • @Autowired和@Resource装配
  • @selector(..)警告提示
  • @Service注解让spring找到你的Service bean
  • [ C++ ] STL_list 使用及其模拟实现
  • [ Linux ] Linux信号概述 信号的产生
  • [ Linux 长征路第五篇 ] make/Makefile Linux项目自动化创建工具
  • [Angular] 笔记 18:Angular Router
  • [Angular] 笔记 8:list/detail 页面以及@Input