likes
comments
collection
share

如何使用React构建一个精确统计用户页面停留时长的Hook?

作者站长头像
站长
· 阅读数 35

前言

Web应用中,了解用户在页面上停留的时间对于优化用户体验和分析用户行为至关重要。本文将基于React的方案实现一个能够精确统计用户在页面上的停留时长,并将这些数据上报到服务器。通过这个方案,我们能够更好地了解用户的行为和优化应用性能。

技术方案

下面就来看看技术方案基于的原理和步骤:

1、原理概述

通过React生命周期、用户活动和浏览器事件,精确统计用户在页面上的停留时长,并将统计结果上报到服务器。

2、步骤描述

  1. 创建一个自定义Hook用于统计停留时长。
  2. 监听用户的活动事件,以判断用户是否与应用互动。
  3. 利用visibilitychange事件检测应用从激活状态到非激活状态的切换。
  4. 在自定义Hook中增加页面地址参数,以便将统计数据与具体页面关联。
  5. 使用Fetch API将统计数据上报到服务器,并处理错误情况和用户反馈。
  6. 处理页面导航,确保数据的准确性。
  7. 尊重用户隐私和获取用户同意,确保数据的合法收集和使用。
  8. 确保代码的可拓展性和可维护性,适应应用规模的增长。

实现步骤

那下面就根据上述的步骤描述来看看具体的实现过程,如下:

1、创建自定义Hook-usePageStayTime

首先创建一个名为usePageStayTime的自定义Hook,用于统计用户在页面上的停留时长。如下:

import { useState, useEffect } from 'react';

const usePageStayTime = () => {
  // 1. 设置状态
  const [stayStartTime, setStayStartTime] = useState(0);
  const [stayEndTime, setStayEndTime] = useState(0);
  const [userActive, setUserActive] = useState(false);
  const [pageUrl, setPageUrl] = useState('');

  // 2. 监听用户活动事件,设置用户活动状态,并记录页面地址
  useEffect(() => {
    // 设置当前页面地址
    setPageUrl(window.location.href);

    // 更新用户活动状态
    const updateUserActivity = () => {
      setUserActive(true);
    };

    // 监听鼠标移动和键盘按键事件
    document.addEventListener('mousemove', updateUserActivity);
    document.addEventListener('keydown', updateUserActivity);

    // 清除事件监听
    return () => {
      document.removeEventListener('mousemove', updateUserActivity);
      document.removeEventListener('keydown', updateUserActivity);
    };
  }, []);

  // 3. 监听应用激活状态变化,记录开始和结束停留时间
  useEffect(() => {
    const handleVisibilityChange = () => {
      if (document.visibilityState === 'visible') {
        // 应用变为可见状态,记录开始停留时间
        setStayStartTime(performance.now());
      } else if (document.visibilityState === 'hidden' && stayStartTime !== 0) {
        // 应用变为不可见状态,记录结束停留时间,并触发上报
        setStayEndTime(performance.now());
      }
    };

    // 监听应用激活状态变化
    document.addEventListener('visibilitychange', handleVisibilityChange);

    // 清除事件监听
    return () => {
      document.removeEventListener('visibilitychange', handleVisibilityChange);
    };
  }, [stayStartTime]);

  // 4. 在停留结束时,上报停留时长数据到服务器
  useEffect(() => {
    if (stayEndTime > stayStartTime && userActive) {
      const stayDuration = stayEndTime - stayStartTime;
      reportStayDurationToServer(stayDuration, pageUrl);
    }
  }, [stayEndTime, stayStartTime, userActive, pageUrl]);

  // 5. 将停留时长数据上报到服务器
  const reportStayDurationToServer = (duration, pageUrl) => {
    // 可用自己项目中封装的请求方法,这里只是演示
    fetch('你的接口地址', {
      method: 'POST',
      body: JSON.stringify({ duration, pageUrl }),
      headers: {
        'Content-Type': 'application/json',
      },
    }).then(data => {
      console.log('数据上报成功', data);
    }).catch(error => {
      console.error('数据上报失败', error);
    });
  };

  // 6. 返回null,因为这是一个Hook
  return null;
};

export default usePageStayTime;

这段代码主要分为以下几个部分:

  1. 状态设置:使用useState来管理停留开始时间、停留结束时间、用户活动状态和页面地址等状态;
  2. 用户活动监听:通过useEffect,在组件挂载时添加了鼠标移动和键盘按键事件的监听,以更新用户活动状态,并记录页面地址;
  3. 应用激活状态监听:使用useEffect监听visibilitychange事件,根据应用的激活状态记录开始和结束停留时间;
  4. 停留时长上报:通过另一个useEffect,当停留结束时,计算停留时长并调用reportStayDurationToServer函数将数据上报到服务器;
  5. 数据上报到服务器:在reportStayDurationToServer函数中,使用fetch API将停留时长数据以及页面地址上报到服务器;
  6. 返回值:由于这是一个Hook,最后返回null。

整个自定义Hook的目的是为了精确地统计用户在页面上的停留时长,并在适当的时候将数据上报到服务器,以便进一步分析和优化应用的性能和用户体验。

2、在应用中使用自定义Hook

接着,就可以在项目中使用这个自定义的Hook了。如下:

import React from 'react';
import usePageStayTime from './usePageStayTime';

const App = () => {
  usePageStayTime();

  return (
      <div>
        {/* 应用内容 */}
      </div>
  );
};

export default App;

3、处理页面导航

用户在应用中进行页面导航时,需要确保停留时长的统计能够正确地跨越页面。所以代码中还需要增加用于处理页面导航情况的代码。如下:

const usePageStayTime = () => {
  // ... 其他状态不变

  // 在页面导航时重置停留时长统计
  useEffect(() => {
    const handleNavigation = () => {
      if (stayStartTime !== 0 && stayEndTime === 0 && userActive) {
        setStayEndTime(performance.now());
        reportStayDurationToServer(stayEndTime - stayStartTime);
      }
      // 重置统计数据
      setStayStartTime(0);
      setStayEndTime(0);
      setUserActive(false);
    };

    window.addEventListener('beforeunload', handleNavigation);

    return () => {
      window.removeEventListener('beforeunload', handleNavigation);
    };
  }, [stayStartTime, stayEndTime, userActive]);

  // ... 其他代码不变
};

这段代码的作用是在用户进行页面导航(如关闭窗口、刷新页面等)时,确保停留时长的统计能够正确处理。具体功能包括:

  1. 定义handleNavigation函数:这个函数是用来处理页面导航事件的。当用户进行页面导航时,首先检查是否满足上报条件:停留开始时间非零、停留结束时间为零且用户活动为true;如果满足条件,我们记录停留结束时间,然后调用reportStayDurationToServer函数将停留时长上报给服务器;随后,重置统计数据,准备进行下一次停留时长的统计。
  2. 添加beforeunload监听器:通过window.addEventListener,在用户进行页面导航之前,将handleNavigation函数绑定到beforeunload事件上。这意味着在页面导航前,会触发handleNavigation函数,从而实现停留时长的统计和数据上报。
  3. 清除监听器:为了避免内存泄漏,在useEffect的返回函数中,使用window.removeEventListener来清除之前添加的beforeunload监听器。这样可以确保在组件卸载或重新渲染时,不会有多余的事件监听器存在。

通过以上的实现,在处理页面导航时,不仅能够确保停留时长数据的准确性,还能将数据及时上报给服务器,为应用性能分析提供有力支持。

4、尊重用户隐私和获取用户同意

在进行数据收集和上报时,需要尊重用户的隐私,并确保在获得用户同意的情况下进行。这方面的做法可能会因应用的性质而有所不同。在数据上报前,可以添加一个用户同意的逻辑,例如弹出一个提示框来告知用户数据的用途,并获得用户的确认。

const usePageStayTime = () => {
  // ...其他状态不变

  const [userConsent, setUserConsent] = useState(false);

  useEffect(() => {
    // 在组件挂载时,检查用户是否已经同意数据收集
    const userHasConsented = localStorage.getItem('userConsent');
    if (userHasConsented === 'true') {
      setUserConsent(true);
    }
  }, []);
  
  useEffect(() => {
    if (stayEndTime > stayStartTime && userActive && userConsent) {
      const stayDuration = stayEndTime - stayStartTime;
      reportStayDurationToServer(stayDuration, pageUrl);
    }
  }, [stayEndTime, stayStartTime, userActive, pageUrl, userConsent]);

  return null;
};

在这段代码中,引入了一个名为userConsent的状态,用于记录用户是否同意数据收集。在组件挂载时,检查本地存储中是否有用户同意的标记,如果有,则设置userConsenttrue。同时,在reportStayDurationToServer函数中,检查userConsent状态,只有在用户已经同意的情况下才进行数据上报。

5、代码的可拓展性和可维护性

当应用规模逐渐增大时,保持代码的可拓展性和可维护性变得尤为重要。以下是一些提升代码质量的方法:

  • 将自定义Hook进行拆分,将不同的功能逻辑分离成多个小的自定义Hook,使代码更具模块化。
  • 使用适当的命名规范,让代码更易于阅读和理解。
  • 添加适当的注释,解释代码的作用和用途,方便团队合作和维护。

通过这些方法,能够保持代码的可拓展性,使应用能够适应不断变化的需求。

思考

不知道你们有没有发现上述实现的Hook在使用上有一个问题,那就是每一个页面都需要引用才能统计到用户页面的停留时长,是不是有点繁复?能不能只在项目中只使用一次就能实现统计呢?

答案肯定是可以的。那就是使用React的上下文(Context)来实现。通过在根组件中引入Hook,并将其提供给整个应用,这样就可以使每个页面都能够访问该Hook的功能,而无需在每个页面中单独引用。

1、创建React上下文

首先创建一个React上下文(PageStayTimeContext.js)。如下:

import React, { createContext, useContext } from 'react';
import usePageStayTime from './usePageStayTime';

const PageStayTimeContext = createContext();

export const PageStayTimeProvider = ({ children }) => {
  const pageStayTimeHook = usePageStayTime();

  return (
      <PageStayTimeContext.Provider value={pageStayTimeHook}>
        {children}
      </PageStayTimeContext.Provider>
  );
};

// 如果想在页面中单独调用usePageStayTime的方法可以单独抛出(非必须)
export const usePageStayTimeContext = () => {
  return useContext(PageStayTimeContext);
};

2、在根组件使用上下文

接着,在App.jsx中使用PageStayTimeProvider提供的上下文。如下:

import React from 'react';
import { PageStayTimeProvider } from './PageStayTimeContext';
import PageComponent from './PageComponent'; // 假设这是页面组件

const App = () => {
  return (
      <PageStayTimeProvider>
        {/* 其他内容 */}
        <PageComponent />
      </PageStayTimeProvider>
  );
};

export default App;

3、页面组件中也可以使用usePageStayTimeContext,无需再次引入Hook(非必须)

import React from 'react';
import { usePageStayTimeContext } from './PageStayTimeContext';

const PageComponent = () => {
  const pageStayTimeHook = usePageStayTimeContext();

  // 页面内容和组件逻辑,可以使用pageStayTimeHook中的函数和状态
};

export default PageComponent;

通过这种方式,就只需要在根组件中引入一次PageStayTimeProvider(前2步即可),然后在需要的页面组件中使用usePageStayTimeContext来获取Hook的功能(非必须)。这样,在项目的根组件中集中管理该功能,而不需要在每个页面中都引入和使用这个Hook

总结

通过本文的内容,我们成功地实现了一个基于React的精确统计用户页面停留时长并将统计结果上报到服务器的完整方案。通过自定义Hook、用户活动事件和数据上报,我们能够了解用户在页面上的停留情况,为优化用户体验和数据分析提供了有力支持。希望本文对您理解如何精确统计页面停留时长并上报数据有所帮助。

后语

小伙伴们,如果觉得本文对你有些许帮助,点个👍或者➕个关注再走吧^_^ 。另外如果本文章有问题或有不理解的部分,欢迎大家在评论区评论指出,我们一起讨论共勉。

转载自:https://juejin.cn/post/7269797709065502775
评论
请登录