如何跟踪 RemixJS 中的综合浏览量?

How to track pageviews in RemixJS?

我正在构建一个 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 的路由,但这没有用,因为 remix 效率很高,并且当用户导航时,它使用 ajax-y 调用仅获取 loaderData而不是从服务器获取完整呈现的页面。我知道这是 Remix 的行为,但在我走这条路之前我不记得了 :facepalm:

据我所知,不可能 stateless-ly 跟踪唯一网页浏览量(即完全基于当前 URL)- 您还需要查看用户的上一页。

我想知道 referer 是否允许它无状态地工作,但似乎 referer 的行为并不像我希望的那样:referer header 已经设置为第一个加载程序请求中的当前页面对于页面的数据。因此,初始加载和 load-after-mutation 根据引用者显示相同。我不知道这在技术上是否是一个错误,但这肯定不是我期望的行为。

我最终通过在客户端进行综合浏览量跟踪解决了这个问题。为了支持在数据库中记录这一点,我实现了一个在位置更改时只接收 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]);

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

客户端网页浏览跟踪有点烦人,因为客户端不稳定,但对于当前的混音行为,我认为这是唯一可行的选择。

它是否以不同的方式用于路由,重新混合很有趣,因为整个父路由的东西*所以我使用路由记录器

Beans.io

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