面试官是怎么知道你把网页切出去偷偷查资料了?

1,486 阅读3分钟

场景

自从疫情之后,大家都已经习惯了线上面试。大多数的线上面试工具通常都会提供检测当前面试者打开的面试页面是否活跃这个状态,如果你长时间处于非活跃状态,面试官有理由怀疑你是不是切页面出去查资料作弊去了!那么这背后的原理究竟是什么?

实现

原理

在页面的正常浏览过程中,通常都会触发以下几种的事件:

  • mousemove:鼠标移动
  • mousedown:点击鼠标键
  • resize:缩放页面
  • keydown:按下键盘按键
  • touchstart:触控屏设备的点击屏幕事件
  • wheel:鼠标滚轮事件

同时浏览器还会有一个事件叫做visibilitychange,如果你隐藏/显示了当前的网页,就会触发这个事件

!!请注意!!

不要以为当你最小化的时候,才叫隐藏页面,假如你在浏览器打开了多个网页,切换标签页的时候,也会触发该事件;如果你的桌面上同时存在多个窗口,但是你并没有聚焦在浏览器上时,也属于隐藏页面,比如下面这种情况,目前聚焦的是设置窗口,并不是打开百度页面的浏览器 image.png

因此基于上面的分析可以知道,如果需要了解当前的用户是否处于活跃状态,直接监听这些事件就可以了。但是监听这些事件只是一部分,通常还需要知道用户距离上一次活跃过去了多久,方便基于非活跃时间做一些处理。针对这些场景,就需要封装一下hooks来简化开发

react中封装useIdle来检测用户活跃状态

由于已经有成熟的开源库,因此笔者直接为大家解读react-use源码:

import { useEffect, useState } from 'react';
import { throttle } from 'throttle-debounce';

// ①
const defaultEvents = ['mousemove', 'mousedown', 'resize', 'keydown', 'touchstart', 'wheel'];
const oneMinute = 60e3;

// ②
const useIdle = (ms = oneMinute, initialState = false, events = defaultEvents) => {
  // ③
  const [state, setState] = useState(initialState);

  // ④
  function on(obj, ...args) {
    if (obj && obj.addEventListener) {
      obj.addEventListener(...args);
    }
  }

  // ⑤
  function off(obj, ...args) {
    if (obj && obj.removeEventListener) {
      obj.removeEventListener(...args);
    }
  }

  useEffect(() => {
    let mounted = true;
    let timeout;
    let localState = state;
    const set = (newState) => {
      if (mounted) {
        localState = newState;
        setState(newState);
      }
    };

    // ⑥
    const onEvent = throttle(50, () => {
      if (localState) {
        set(false);
      }

      clearTimeout(timeout);
      timeout = setTimeout(() => set(true), ms);
    });
    const onVisibility = () => {
      if (!document.hidden) {
        onEvent();
      }
    };

    // ⑦
    for (let i = 0; i < events.length; i++) {
      on(window, events[i], onEvent);
    }
    
    // ⑧
    on(document, 'visibilitychange', onVisibility);

    timeout = setTimeout(() => set(true), ms);

    return () => {
      // ⑨
      mounted = false;

      for (let i = 0; i < events.length; i++) {
        off(window, events[i], onEvent);
      }
      off(document, 'visibilitychange', onVisibility);
    };
  }, [ms, events]);

  return state;
};

export default useIdle;

①:存储了之前介绍的常见事件,并将其作为默认监听事件

②:用户使用钩子时需要传入

  • ms:检测用户离开页面ms毫秒后,状态变为非活跃状态,默认为一分钟
  • initialState:当前用户初始化的是否活跃状态,默认为非活跃
  • events:如果你认为默认监听的事件无法满足你的需求,可以自定义

③:存储当前用户是否活跃的状态

④:封装了一个事件监听的方法

⑤:封装了一个移除事件监听的方法

⑥:将监听函数执行的方法,进行节流调用(提升性能),设置为50ms

⑦:在window上监听设定好的默认事件

⑧:请注意!!为了兼容性,请将visibilitychange的事件监听挂载在document上,不要将其挂载在window上。

⑨:在卸载时取消监听,释放内存。

最后,笔者还是奉劝大家,好好学习天天向上。不要在面试的时候小偷小摸 开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 13 天,点击查看活动详情