一. rxjs
介绍
rxjs
是一个函数式编程的工具库,即推崇纯函数的开发理念。纯函数的概念这里不过多展开,读者可以自行查阅。简单理解就是不依赖this
,稳定的输入输出。
代码示例如下
javascript
import { Observable, map } from 'rxjs'
const observable = new Observable(subscriber => {
subscriber.next(1)
}).pipe(map(x => x * 2))
observable.subscribe(x => {
console.log(x)
})
二. rxjs
原理
rxjs
核心原理是实现了一套类似EventEmitter
的事件订阅/派发更新的机制,且结合懒执行/惰性执行的机制优化性能。在下文实现fromEvent
这个API
时会详细说明。
rxjs
有两个核心对象,一个是Observable
,用于收集事件监听方法,另一个是Subscriber
,用于收集事件派发方法。
2.1 定义Observable
对象原型
Observable
对象核心属性_subscribe
记录事件监听方法,核心方法subscribe
创建Subscriber
对象实例,收集事件派发方法,执行事件监听处理逻辑。
javascript
function Observable(subscribe) {
if (subscribe) this._subscribe = subscribe
}
Observable.prototype.subscribe = function (observerOrNext, error, complete) {
const subscriber = new Subscriber(observerOrNext, error, complete)
const operator = this.operator
const source = this.source
subscriber.add(
operator
? operator.call(subscriber, source)
: this._trySubscribe(subscriber),
)
}
Observable.prototype._trySubscribe = function (subscriber) {
try {
return this._subscribe(subscriber)
} catch (err) {
subscriber.error(err)
}
}
2.2 定义Subscriber
对象原型
Subscriber
对象的partialObserver
属性用于收集派发更新方法,是一个对象,核心属性有next
、error
和complete
,其中next
属性是必传的。
与EventEmitter
类似,提供了类似off
方法的逻辑解除事件监听逻辑,主要通过add
和unsubscribe
方法,add
方法用于收集解除事件监听方法,unsubscribe
方法用于执行解除事件监听方法。
javascript
function Subscriber(observerOrNext, error, complete) {
// 是否终止执行next、error、complete事件
this.isStopped = false
this.closed = false
this._finalizers = []
if (isFunction(observerOrNext)) {
this.partialObserver = {
next: observerOrNext,
error,
complete,
}
} else {
this.partialObserver = observerOrNext
}
}
Subscriber.prototype.next = function (value) {
if (this.isStopped) return
if (this.partialObserver.next) this.partialObserver.next(value)
}
Subscriber.prototype.error = function (err) {
if (this.isStopped) return
if (this.partialObserver.error) this.partialObserver.error(err)
}
Subscriber.prototype.complete = function () {
if (this.isStopped) return
this.isStopped = true
if (this.partialObserver.complete) this.partialObserver.complete()
}
Subscriber.prototype.add = function (finalizer) {
if (finalizer) {
if (this.closed) {
finalizer()
} else {
this._finalizers.push(finalizer)
}
}
}
Subscriber.prototype.unsubscribe = function () {
if (this.closed) return
this.closed = true
this.isStopped = true
this._finalizers.forEach(cb => cb())
}
2.3 实现fromEvent
fromEvent
方法作用是添加DOM
事件监听。代码逻辑比较简单,核心是学习懒执行的思想。
javascript
function isEventTarget(target) {
return (
isFunction(target.addEventListener) &&
isFunction(target.removeEventListener)
)
}
function isNodeStyleEventEmitter(target) {
return isFunction(target.addListener) && isFunction(target.removeListener)
}
const eventTargetMethods = ['addEventListener', 'removeEventListener']
const nodeEventEmitterMethods = ['addListener', 'removeListener']
export function fromEvent(target, eventName, options) {
const listeners = (
isEventTarget(target)
? eventTargetMethods
: isNodeStyleEventEmitter(target)
? nodeEventEmitterMethods
: []
).map(
method =>
function (handler) {
target[method](eventName, handler, options)
},
)
if (!listeners.length) throw new TypeError('Invalid event target')
const addEventListener = listeners[0]
const removeEventListener = listeners[1]
return new Observable(subscriber => {
const handler = function (...args) {
return subscriber.next(...args)
}
addEventListener(handler)
return () => removeEventListener(handler)
})
}
结合下面代码示例,解析整体执行流程:
首先调用fromEvent
方法时,会创建Observable
对象实例,收集添加DOM
事件监听方法,需要注意的是此时还没有执行事件绑定逻辑的,当调用observable.subscribe
方法时,才执行DOM
事件监听逻辑,同时收集事件回调方法。假如这时触发click
事件,会执行handler
方法,调用subscriber.next
方法执行事件回调。
这里懒执行的思想主要体现在DOM
事件监听的执行时机不是一开始就执行,而是当添加订阅事件回调时才执行DOM
事件监听逻辑。
javascript
import { fromEvent } from 'rxjs'
const observable = fromEvent(document, 'click')
observable.subscribe(evt => {
console.log(evt)
})
2.4 实现函数式编程
rxjs
提供了丰富的API
,可以通过pipe
方法进行API
的组装,举个🌰,类似工厂流水线,流水线中包含多道工序,可以根据业务场景自由增删工序。
例如下面这段代码,数据会先经过filter
处理,符合条件的数据会流转map
处理,最终流转到业务事件回调。
javascript
import { filter, map, Observable } from 'rxjs'
const observable = new Observable(subscriber => {
subscriber.next(1)
}).pipe(
filter(x => x),
map(x => x * 2),
)
observable.subscribe(x => {
console.log(x)
})
2.4.1 实现pipe
方法
pipe
方法入参operations
是数组类型,元素值是API
方法,会循环遍历数组元素,创建其对应的Observable
对象实例,observable.source
指向上一个Observable
对象实例,observable.operator
指向API
的执行方法。
javascript
Observable.prototype.lift = function (operator) {
const observable = new Observable()
observable.source = this
observable.operator = operator
return observable
}
Observable.prototype.pipe = function (...operations) {
return (function piped(input) {
return operations.reduce((prev, operation) => operation(prev), input)
})(this)
}
2.4.2 实现map
和filter
rxjs
的API
很多,这里只实现map
和filter
,了解其核心原理即可。每个API
都对应一个Observable
对象实例,会调用其subscribe
方法添加事件派发更新回调,当触发顶层Observable
对象的_subscribe
方法时,则会依次调用API
代码逻辑。
javascript
function operate(init) {
return function (source) {
return source.lift(function (liftedSource) {
try {
return init(liftedSource, this)
} catch (err) {
this.error(err)
}
})
}
}
function filter(cb, thisArg) {
return operate(function (source, subscriber) {
source.subscribe(function (value) {
if (cb.call(thisArg, value)) subscriber.next(value)
})
})
}
function map(cb, thisArg) {
return operate(function (source, subscriber) {
source.subscribe(function (value) {
subscriber.next(cb.call(thisArg, value))
})
})
}
三. 总结
rxjs
是一个优秀的函数式编程框架,如果业务中有使用函数式编程的诉求,那可以考虑使用rxjs
。最重要的是学习rxjs
的核心理念,即事件订阅/派发更新的设计模式,配合懒执行机制优化性能。另外就是函数式编程的理念。代码仓库
创作不易,如果文章对你有帮助,那点个小小的赞吧,你的支持是我持续更新的动力!