RxJS订阅重复触发两次问题修复(webpack环境)
解决Webpack环境下RxJS订阅回调重复输出的问题
问题场景
在Webpack环境中使用RxJS监听input输入事件,调用GitHub用户搜索接口后,发现subscribe回调里同一对象被输出两次,第二次响应来自Webpack生成的临时代码(如示例中的VM1023 index.js.35)。
代码示例
import { fromEvent, EMPTY } from 'rxjs' import {map, debounceTime, tap, distinctUntilChanged, switchMap, catchError, filter, mergeMap} from 'rxjs/operators' import {ajax} from 'rxjs/ajax' import css from './styles.css' const url = ' https://api.github.com/search/users?q=' const input = document.querySelector('.input') const result = document.querySelector('.result-container') const stream$ = fromEvent(input, 'input').pipe( map(value => value.target.value), debounceTime(1000), distinctUntilChanged(), filter(v => v.trim()), switchMap(value => ajax.getJSON(url + value).pipe( catchError(() => EMPTY) )), map(items => items.items), mergeMap(item => item) ) stream$.subscribe(value => { console.log(value) })
问题原因
这是Webpack的**热模块替换(HMR)**机制导致的:当你修改代码后,HMR会重新加载当前模块,但之前模块中创建的Observable订阅并未被取消,新模块又会创建一个全新的订阅,最终同一个事件会触发两次回调。
解决方法
方法一:关闭热模块替换(HMR)
如果开发环境不需要HMR功能,直接在Webpack配置文件中关闭相关设置:
- 将
devServer.hot设置为false - 移除配置中的
HotModuleReplacementPlugin插件
方法二:手动管理订阅,模块替换时取消订阅
保存订阅实例,在HMR销毁当前模块时主动取消订阅:
// ...之前的代码 const stream$ = fromEvent(input, 'input').pipe( // ...之前的操作符 ) // 保存订阅实例 const subscription = stream$.subscribe(value => { console.log(value) }) // 监听HMR销毁事件,取消订阅 if (module.hot) { module.hot.dispose(() => { subscription.unsubscribe() }) }
方法三:用RxJS操作符自动管理订阅
借助takeUntil操作符,配合HMR的销毁信号自动结束Observable:
import { fromEvent, EMPTY, Subject } from 'rxjs' import {map, debounceTime, distinctUntilChanged, switchMap, catchError, filter, mergeMap, takeUntil} from 'rxjs/operators' // ...其他导入 // 创建一个Subject用于发送销毁信号 const hmrDestroy$ = new Subject() // 监听HMR销毁事件,发送结束信号 if (module.hot) { module.hot.dispose(() => { hmrDestroy$.next() hmrDestroy$.complete() }) } const stream$ = fromEvent(input, 'input').pipe( map(value => value.target.value), debounceTime(1000), distinctUntilChanged(), filter(v => v.trim()), switchMap(value => ajax.getJSON(url + value).pipe( catchError(() => EMPTY) )), map(items => items.items), mergeMap(item => item), // 当收到销毁信号时,自动结束Observable takeUntil(hmrDestroy$) ) stream$.subscribe(value => { console.log(value) })
内容的提问来源于stack exchange,提问作者Abdugafor Gafarov
相关产品推荐
相关产品推荐

