dynsxyc 2019-06-28
最近组内的angular在做4->6的升级,这当中也涉及到了rxjs的升级。rxjs升级guide链接以下是记录的一些rxjs的升级小tips
6版本有非常多的break points,总结下来为以下三点:
以上几点,在具体实践上,1和2根据api,以及rx提供的升级检查工具,都能比较愉快的完成,但比较有意义的第3点,作为一个对函数式编程感兴趣的前端码农,当然要研究一下原委。
其实1和2都有点为第三点服务的意思,我们先简单说下1和2。引用路径的变更,意味着源码目录结构的调整(这些大家可以下载源码,自行看下);同时,原来的rx也采用了和underscore、lodash一样的导出方式,在写法上,满足chainable,所以用rx5时,我们是这样写的:
var Rx = require('rxjs'); const Observable = Rx.Observable; Observable.range(1,10) .filter(x => x % 2 === 0) .map(x => x + x) .subscribe(x=> console.log(x))
v5的rx导出了一个核心对象Observable以及若干的附属类型对象,然后方法(自有方法(create、subscribe等),以及一些常用的操作符(map、fileter等))定义在这些对象的prototype上,所以这种定义方式搞起链式调用就666,这种实现不影响其本质上的函数式,但在写法上其实是面向对象的伪函数式,但但是奇奇怪怪的,老是搞对象是什么鬼,说好的FP呢。
所以这也是今天要说的一个重点,chainable-->pipeable的实现,是rx这次版本升级最根本的地方。在内部实现上,把原有的操作符都函数化,同时在Observable上新增了pipe方法,以下是pipe.ts中的pipe实现
export function pipe<T, R>(...fns: Array<UnaryFunction<T, R>>): UnaryFunction<T, R> { return pipeFromArray(fns); } /* @internal */ export function pipeFromArray<T, R>(fns: Array<UnaryFunction<T, R>>): UnaryFunction<T, R> { if (!fns) { return noop as UnaryFunction<any, any>; } if (fns.length === 1) { return fns[0]; } return function piped(input: T): R { return fns.reduce((prev: any, fn: UnaryFunction<T, R>) => fn(prev), input); }; }
熟悉FP的同学会发现,这个pipe其实是一个从左向右执行的compose,只不过它接受的第一个参数是this,即当前Observable实例,所以我们第一个例子在v6中得这么写:
import { range } from 'rxjs/observable/range'; import { map, filter } from 'rxjs/operators'; range(0, 10).pipe( filter(x => x % 2 === 0), map(x => x + x) ).subscribe(x=> console.log(x))
上例pipe做的事情等同于:mapFn(filterFn(range(0,10)))
chainable==>到pipeable,在写法上是一次更加彻底的函数式实践。
当然这种方法->函数的更改,还有一些更大的好处:
其它更详尽的点,可参考: