我正在构建一个 Remix 应用程序,并希望根据用户正在查看的页面在我的数据库中记录一些用户分析。我还想逐个路由地执行此操作,而不仅仅是简单地使用原始 URL。
例如:我想知道“用户查看的 URL /emails/123”以及“用户查看的路由 /emails/$emailId”
这个问题可以概括为“我想为每个用户导航运行一段服务器代码”
对于我的跟踪,我假设用户在其浏览器中启用了 JavaScript。
这会是这样的:
export const loader: LoaderFunction = async ({ request, params }): Promise<LoaderData> => {
myDb.recordPageVisit(request.url);
}
Run Code Online (Sandbox Code Playgroud)
这不起作用,因为每次页面访问都可以多次调用加载程序(例如,在运行操作后)
请求参数中可能隐藏了一些值,告诉我们这是初始页面加载还是稍后访问,但如果是这样,我找不到它,包括当我检查原始 HTTP 请求时。
必须将此代码放入每个加载器中也很烦人。
我使用 @remix-run/node 作为我的基础混音服务器,所以我有设置节点中间件的逃生舱口,我认为这可能是一个很好的答案:
const app = express();
app.use((req, res, next) => {
if (req.url.indexOf("_data") == -1) {
myDb.recordPageVisit(req.url);
}
next();
});
Run Code Online (Sandbox Code Playgroud)
我尝试忽略_data其中的路由,但这不起作用,因为重新混合非常高效,并且当用户导航时,它使用 ajax-y 调用仅获取 loaderData 而不是从服务器获取完整渲染的页面。我知道这是 Remix 的行为,但在我走这条路之前我不记得它:facepalm:
据我所知,无状态地跟踪唯一的页面浏览量(即纯粹基于当前的 URL)是不可能的 - 您还需要查看用户的上一页。
我想知道引用者是否允许它无状态地工作,但引用者似乎没有按照我希望的方式运行:在第一个加载器请求页面数据时,引用者标头已经设置为当前页面。因此,基于引用者,初始加载和突变后加载看起来是相同的。我不知道这在技术上是否是一个错误,但这肯定不是我期望的行为。
我最终通过在客户端中进行页面浏览跟踪来解决这个问题。为了支持在数据库中记录这一点,我实现了一个在位置更改时仅接收 POST 的路由。
React-router 的 useLocation 的文档实际上包含了这个确切的场景作为示例。
来自https://reactrouter.com/docs/en/v6/api#uselocation:
function App() {
let location = useLocation();
React.useEffect(() => {
ga('send', 'pageview');
}, [location]);
return (
// ...
);
}
Run Code Online (Sandbox Code Playgroud)
但是,这在重新混合中不太有效 - 位置值在操作后发生变化(相同的文本值,但可能不同的参考值)。因此,我开始保存最后看到的位置字符串,然后仅在位置字符串值发生更改时报告新的综合浏览量。
因此,在添加当前位置的状态跟踪后,我发现:
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]);
Run Code Online (Sandbox Code Playgroud)
匹配代码对于仅跟踪原始 URL 不是必需的,但我想提取路由形式(例如 /emails/$emailId),并且 matches.id 与该值非常匹配 - 我剥离“routes/”服务器端。匹配文档:https://remix.run/docs/en/v1/api/remix#usematches
客户端页面浏览量跟踪有点烦人,因为客户端不稳定,但对于当前的混音行为,我相信这是唯一真正的选择。
| 归档时间: |
|
| 查看次数: |
5886 次 |
| 最近记录: |