Определение отсутствия активности пользователей

Иногда отслеживание активности пользователя может быть полезным. Например, для автоматического выхода пользователей из системы по соображениям безопасности.

Почему стоит беспокоиться об автоматическом выходе?

Многие приложения предоставляют пользователям доступ к их личным данным. Но некоторые пользователи могут забыть выйти из системы и оставить сеанс открытым. Это опасно, поскольку кто-то другой может использовать этот сеанс для получения конфиденциальных данных.

Данную проблему может решить отслеживание того, взаимодействует ли пользователь с приложением в течение определенного периода времени. Если это время превышено, происходит автоматический выход из системы.

В этой статье я покажу, как реализовать подобную ​​логику отслеживания активности пользователя на основе следующего примера.

Шаг 1: Реализация логики отслеживания

Создадим две функции. Первая будет отвечать за сброс таймера при каждом взаимодействии пользователя с приложением, а вторая – обрабатывать ситуацию, когда пользователь становится неактивным:

  • resetUserActivityTimeout — это метод, который отвечает за сброс существующего счетчика и запуск нового при каждом взаимодействии пользователя с приложением.
  • inactiveUserAction – метод, который запускается по истечении времени ожидания активности пользователя.
let userActivityTimeout = null;

function resetUserActivityTimeout() {
  clearTimeout(userActivityTimeout);
  userActivityTimeout = setTimeout(() => {
    inactiveUserAction();
  }, INACTIVE_USER_TIME_THRESHOLD);
}

function inactiveUserAction() {
  // логика выхода из системы
}

Шаг 2. Отслеживание активности

Теперь создадим методы, которые отвечают за отслеживание активности пользователя. В них мы добавим прослушиватели, которые при обнаружении события будут вызывать метод resetUserActivityTimeout. Для простоты мы ограничимся несколькими наиболее распространенными событиями.

function activateActivityTracker() {
  window.addEventListener("mousemove", resetUserActivityTimeout);
  window.addEventListener("scroll", resetUserActivityTimeout);
  window.addEventListener("keydown", resetUserActivityTimeout);
  window.addEventListener("resize", resetUserActivityTimeout);
}

Теперь осталось вызвать activateActivityTracker на странице. Но в приведенном выше коде есть серьезная проблема производительности. Каждый раз, когда пользователь взаимодействует с приложением, запускается вся логика. Но некоторые события запускаются огромное количество раз, даже если это не является необходимым.

Рассмотрим событие mousemove. Даже если вы перемещаете мышку одним касанием, событие mousemove будет запущено десятки раз. Можно решить эту проблему, введя регулятор, который позволит логике отслеживания активности пользователя запускаться только один раз за указанный период времени.

Шаг 3: Улучшение производительности

Сначала добавим еще одну переменную, которая будет хранить ссылку на время ожидания регулятора.

let userActivityThrottlerTimeout = null

Затем реализуем метод, который создаст регулятор. Он проверяет, существует ли счетчик таймера, а если нет, то запускаем resetUserActivityTimeout через определенный промежуток времени. Это период, за который активность пользователя не будет повторно вызывать логику отслеживания. По истечении этого времени счетчик регулятора сбрасывается. Что позволяет следующему взаимодействию сбросить отслеживание активности пользователя.

userActivityThrottler() {
  if (!userActivityThrottlerTimeout) {
    userActivityThrottlerTimeout = setTimeout(() => {
      resetUserActivityTimeout();

      clearTimeout(userActivityThrottlerTimeout);
      userActivityThrottlerTimeout = null;
    }, USER_ACTIVITY_THROTTLER_TIME);
  }
}

Мы создали новый метод, который должен запускаться при взаимодействии с пользователем. Поэтому изменим обработчик событий с resetUserActivityTimeout на userActivityThrottler.

activateActivityTracker() {
  window.addEventListener("mousemove", userActivityThrottler);
  // ...
}

Бонус: применим это с Vue!

Теперь посмотрим, как импортировать эту логику в сборку приложения с помощью Vue. Мы будем использовать этот пример.

Сначала переместим все переменные в компоненты data.

export default {
  data() {
    return {
      isInactive: false,
      userActivityThrottlerTimeout: null,
      userActivityTimeout: null
    };
  },
// ...

Затем переместим все функции в methods:

// ...
  methods: {
    activateActivityTracker() {...},
    resetUserActivityTimeout() {...},
    userActivityThrottler() {...},
    inactiveUserAction() {...}
  },
// ...

Поскольку мы используем Vue, можно отбросить все прямые манипуляции с DOM и полагаться на свойство данных isInactive. Мы можем получить доступ к свойству data непосредственно в шаблоне компонента.

<template>
  <div id="app">
    <p>User is inactive = {{ isInactive }}</p>
  </div>
</template>

Vue поставляется с хуками жизненного цикла компонентов. Используем хук beforeMount.

// ...
  beforeMount() {
    this.activateActivityTracker();
  },
// ...

Теперь удалим все прослушиватели, которые мы зарегистрировали, и очистим все таймауты, когда жизненный цикл компонента закончится.

// ...
  beforeDestroy() {
    window.removeEventListener("mousemove", this.userActivityThrottler);
    window.removeEventListener("scroll", this.userActivityThrottler);
    window.removeEventListener("keydown", this.userActivityThrottler);
    window.removeEventListener("resize", this.userActivityThrottler);
  
    clearTimeout(this.userActivityTimeout);
    clearTimeout(this.userActivityThrottlerTimeout);
  }
// ...

Это все!

В рассмотренном нами примере основное внимание уделяется обнаружению взаимодействия пользователя с приложением. А также запуску метода, когда взаимодействие не обнаружено в течение определенного периода времени.

Вадим Дворниковавтор-переводчик статьи «Detecting Inactive Users»