我需要实现一种在浏览器(Angular 应用程序)中跟踪空闲状态/不活动的方法,因此我try 创建一个观察值,只要没有任何用户交互事件就会触发,这样客户端就可以自动注销:

import {filter, from, fromEvent, mergeAll, Observable, of, repeat, timeout} from 'rxjs';

const timeoutDelay = 1000 * 60 * 5; // 5 minutes
const events = ['keypress', 'click', 'wheel', 'mousemove', 'ontouchstart'];    
const $onInactive = from(events.map(e => fromEvent(document, e)))
  .pipe(
    mergeAll(),
    timeout({each: timeoutDelay, with: () => of(undefined as any)}),
    filter(a => !a),
    repeat()
);

虽然我能够使用上面的代码使它工作,但我不确定对于这个特定的任务,这是否正确或有效地完成,所以我只想在这里再次判断.

客户端将使用如下代码:

$onInvactive.subscribe(() => {
    // no user activity for 5 minutes

    // if currently logged in, then log out
});

在Web客户端的整个生命周期中,只要发生不活动,通知就会到达.

推荐答案

That's actually a very good question!
Your approach for tracking certain activity events is clever, but in the end it really depends on your definition of "inactivity". If your are happy with your current definition, then you can probably track a few more like scroll or resize.

另一个"不活动"定义可以是"只要标签是活动的,用户就是活动的".在现代浏览器中,这可以通过文档中的Page Visibility API AKE LISTEN来跟踪visibilitychange事件.在窗口对象上使用窗口焦点和模糊事件也应该是一种解决方法.

现在,谈到实现本身,我将建议一种简化的方法.

const browserInactive$ = (graceTime: number): Observable<Event> => {
    const activityIndicatorEvents = ['click', 'mousemove', 'mousedown', 'scroll', 'keypress'];
    return merge(...activityIndicatorEvents.map(eventName => fromEvent(document, eventName))).pipe(debounceTime(graceTime));
};

我自己还没有测试过这一点,但 idea 很简单.它遵循您最初的实现,但我们使用debounceTime作为等待graceTime"到期"的机制,并使用函数作为配置行为的方便方法.

编辑1: 我有了一个 idea ,可以让您的Angular 组件或服务更容易地使用可观察对象

export const BROWSER_INACTIVE$ = new InjectionToken<Observable<Event>>('Browser inactivity detector', {
    factory: () => {
        const activityIndicatorEvents = ['click', 'mousemove', 'mousedown', 'scroll', 'keypress'];
        const graceTime = 1000 * 60 * 5; // 5 minutes
        return merge(...activityIndicatorEvents.map(eventName => fromEvent(document, eventName))).pipe(debounceTime(graceTime), share());
    }
});

然后可以像这样使用它:

@Component({...})
export class SomeComponent {
  constructor(
    @Inject(BROWSER_INACTIVE$) private readonly browserInactive$: Observable<Event>
  ) {}
}

编辑2:基于业务反馈. 在这种情况下,我们可以使用稍微不同的方法:

const browserInactive$ = (graceTime: number): Observable<any> => {
  const activityIndicatorEvents$ = merge(
      ...['click', 'mousemove', 'mousedown', 'scroll', 'keypress'].map(
          (eventName) => fromEvent(document, eventName)
      )
  );

  return interval(graceTime).pipe(
      takeUntil(activityIndicatorEvents$),
      repeat()
  );
};

现在的 idea 是,我们有某种类型的timer(实际上是interval),它将每gracePeriod次emits 一次,除非检测到activityIndicator事件.问题是takeUntil将完成流,因此我们需要使用repeat来使其保持活动状态:)

Angular相关问答推荐

使用注入的主机组件进行指令测试

声明Angular material 中按钮的自定义 colored颜色

路由在全局导航中总是给我/甚至使用相对路径

向Anguar 17项目添加服务以启动和停止Loader-NGX-UI-Loader

将ngModel绑定到@Input属性是不是一种糟糕的做法?

自定义垫日历中选定的日期(Angular material )

Angular 信号 - 使用 mutate() 与使用 forEach() react 性

使用 primeng Apollo 主题进行实时部署时多次加载 theme.css 和 preloading.css 文件

Angular 等待对话框未显示

无法将项目从 Angular 13 更新到 14

不再需要 core-js 了吗?

如何为所有模块全局声明指令?

如何在两个模块之间共享服务 - @NgModule 在Angular 而不是在组件之间?

将 Angular 5 升级到 6 时,我得到不兼容的对等依赖(使用 ng update @angular/core)

@angular/platform-b​​rowser 与 @angular/platform-b​​rowser-dynamic

angular2中的httpinterceptor类似功能是什么?

MatToolbar 与 Angular 9 一起使用时抛出错误

无法绑定到 target,因为它不是div的已知属性

Angular 应用程序必须在新部署后清除缓存

ng build 时超出调用重试次数异常