You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 07:52:43