【问题标题】:How to track pageviews in RemixJS?如何在 RemixJS 中跟踪页面浏览量?
【发布时间】:2022-07-18 22:50:17
【问题描述】:

我正在构建一个 Remix 应用程序,并希望根据用户正在查看的页面在我的数据库中记录一些用户分析。我还想逐个路由地这样做,而不仅仅是原始 URL。

例如:我想知道“用户查看的 URL /emails/123”以及“用户查看的路由 /emails/$emailId”

这个问题可以概括为“我想为每个用户导航运行一次服务器代码”

对于我的跟踪,我假设用户在他们的浏览器中启用了 javascript。

我尝试过的解决方案:

在加载器中记录

这可能是这样的:

export const loader: LoaderFunction = async ({ request, params }): Promise<LoaderData> => {
  myDb.recordPageVisit(request.url);
}

这不起作用,因为每次页面访问都可以多次调用加载器(例如,在运行操作之后)

请求参数中可能隐藏了一些值,告诉我们这是初始页面加载还是稍后访问,但如果是这样,我找不到它,包括我检查原始 HTTP 请求时。

不得不把这段代码放在每个加载器中也很烦人。

在节点代码中记录URL

我使用@remix-run/node 作为我的基础混音服务器,所以我有设置节点中间件的逃生舱,我认为这可能是一个很好的答案:

  const app = express();
  app.use((req, res, next) => {
    if (req.url.indexOf("_data") == -1) {
      myDb.recordPageVisit(req.url);
    }
    next();
  });

我尝试忽略其中包含 _data 的路由,但这不起作用,因为重新混合效率很高,并且当用户导航时,它使用 ajax-y 调用仅获取 loaderData 而不是获取完整渲染来自服务器的页面。我知道这是 Remix 的行为,但在我走这条路之前我不记得了:facepalm:

据我所知,不可能无状态地跟踪唯一的网页浏览量(即纯粹基于当前 URL)——您还需要查看用户的上一页。

我想知道referer 是否允许它无状态地工作,但看起来referer 的行为并不像我希望的那样:在第一个加载程序请求页面数据时,referer 标头已经设置为当前页面.因此,基于引用者,初始加载和变异后加载看起来是相同的。我不知道这在技术上是否是一个错误,但这肯定不是我所期望的行为。

【问题讨论】:

  • 为了整合谷歌分析,我引用了存储库中的示例:github.com/remix-run/remix/tree/main/examples/google-analytics
  • 另一种替代方法是使用 Fathom,这是一种尊重隐私的 Google Analytics 替代方法,对欧盟友好,请参阅 usefathom.com — 了解 Kent Dodds 网站如何使用 fathom-client in entry.client.tsx: @987654322 @

标签: remix.run


【解决方案1】:

我最终通过在客户端中进行综合浏览量跟踪解决了这个问题。为了支持在数据库中记录这一点,我实现了一个在位置更改时刚刚收到 POST 的路由。

react-router 的 useLocation 文档实际上包含了这个确切的场景作为示例。

来自https://reactrouter.com/docs/en/v6/api#uselocation

function App() {
  let location = useLocation();

  React.useEffect(() => {
    ga('send', 'pageview');
  }, [location]);

  return (
    // ...
  );
}

但是,这在 remix 中并不完全有效 - 位置值在操作后发生更改(相同的文本值,但可能不同的 ref 值)。所以我开始保存最后看到的位置字符串,然后只在位置字符串值发生变化时报告新的浏览量。

所以在添加了当前位置的有状态跟踪之后,我登陆了:

export default function App() {
  // ...other setup omitted...
  const [lastLocation, setLastLocation] = useState("");
  let location = useLocation();
  const matches = useMatches();

  useEffect(() => {
    if (lastLocation == location.pathname) {
      return;
    }
    // there are multiple matches for parent route + root route, this 
    // will give us the leaf route
    const routeMatch = matches.find((m) => m.pathname == location.pathname);
    setLastLocation(location.pathname);
    fetch("/api/pageview", {
      body: JSON.stringify({ 
        url: location.pathname,
        // routeMatch.id looks like: "/routes/email/$emailId"
        route: routeMatch?.id }),
      method: "POST",
    }).then((res) => {
      if (res.status != 200) {
        console.error("could not report pageview:", res);
      }
    });
  }, [location]);

matches 代码对于跟踪原始 URL 不是必需的,但我想提取路由表单(例如 /emails/$emailId),matches.id 与该值非常匹配 - 我去除了“routes/”服务器端。匹配文档:https://remix.run/docs/en/v1/api/remix#usematches

客户端浏览量跟踪有点烦人,因为客户端不稳定,但对于当前的混音行为,我相信这是唯一真正的选择。

【讨论】:

  • 谢谢,正在寻找处理这个问题的好方法。
【解决方案2】:

用不同的方式处理路由,混音很有趣,因为整个父路由的东西* 所以我使用路由记录器

Beans.io

https://www.npmjs.com/package/beansio

【讨论】:

  • 这听起来与我在“在节点代码中记录 URL”中尝试的类似,在那里我遇到了无法区分初始页面加载和以后加载的问题行动后的数据 - 豆类是否有区分这些的技巧?我没有看到源链接来了解它是如何工作的。
  • 是的,bean 确实有一种区分路由的方法,它了解 remix 创建路由的方式,并且具有能够即时理解它的功能,例如 test/$Id.player.$food 会记录比如 /test/:id/player/:food
  • 我在使用基于路由的记录器时看到的是,单个页面访问看起来像是对同一路由的多次访问,因为加载器在操作后被调用。如果用户访问一个页面,然后在该页面上完成一项操作,它会记录一个事件还是多个事件?
  • 根据中间件的添加方式,我不认为这个解决方案有这个问题,你可以尝试一下,虽然这里是你可以查看的 github repo。 github.com/virgincodes/beansio
  • 感谢分享代码。不幸的是,这似乎并不能避免“用户采取行动时多次记录”的问题。这个问题是关于网页浏览量,而不是操作。最终,您在这里所做的与我在“在节点代码中记录 URL”被解雇的解决方案中所写的并没有什么不同。 beansio 似乎是在路由记录器上添加一些元数据/跟踪给定用户会话的好方法。我个人对通过读取文件系统来重新创建 remix 的路由创建逻辑有点犹豫,因为这似乎很脆弱。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多