npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@arms/rum-browser-react

v0.1.2

Published

React plugin for @arms/rum-browser SDK (route view.name convergence + React error boundary + hooks)

Readme

@arms/rum-browser-react

@arms/rum-browser SDK 的 React 插件 —— 提供路由驱动的 view.name 收敛(startView 手动 view 模式)、React ErrorBoundary 集成,以及用于自定义事件上报的 useRum Hook。

安装

本插件不独立工作 —— 需与主包 @arms/rum-browser(必需 peer dependency)同时安装,并完成主包初始化(见「快速开始」):

npm install @arms/rum-browser @arms/rum-browser-react

Peer dependencies

| Package | Version | Required | | ------------------- | ------------------------ | -------- | | @arms/rum-core | >=0.1.11 | Yes | | @arms/rum-browser | >=0.1.16 | Yes | | react | >=16.8 <20 | Yes | | react-router | 5 \|\| 6 \|\| 7 \|\| 8 | Optional | | react-router-dom | 5 \|\| 6 \|\| 7 | Optional |

react-router / react-router-dom 为可选 peer —— 仅在使用路由集成功能时需要。请安装与你所使用的 router 版本对应的子路径入口(v5/v6/v7/v8 各有独立入口)。

注意:react-router v8 已移除 react-router-dom 包,DOM 专用 API(如 RouterProvider)改从 react-router/dom 导入;本插件的 react-router-v8 入口导出的路由包装 API 与 v7 入口完全一致(re-export v7 实现)。

history@^4.9.0 为本包的直接依赖(dependencies,随包安装):仅 react-router-v5 入口使用(包装 Router 系列需自建 history 实例)。以直接依赖而非 optional peer 安装,可保证在 pnpm 严格隔离、或宿主应用自带 history@5(react-router v6 data router 场景)等环境下,v5 入口始终解析到 history@4;npm/yarn hoist 场景下与 react-router-dom@5 自带的 history@4 传递依赖等价。

webpack4 兼容:四个子路径入口(react-router-v5 / -v6 / -v7 / -v8)均在包根提供同名 CJS 存根(react-router-v5.js 等,包含于 files),不读取 exports 字段的旧构建链(webpack 4、部分 CDN 拼接工具)可直接 require('@arms/rum-browser-react/react-router-v5') 解析;同时提供同名根级 .d.ts 类型存根(react-router-v5.d.ts 等,export * 转发 es/entries/ 入口全部具名导出,包含于 files),moduleResolution: "node"(经典模式,不读 exportstypes 条件)的 TS 工程亦有完整类型,子路径 API 不会回落 any

快速开始

1. 安装主包与插件

npm install @arms/rum-browser @arms/rum-browser-react

2. 初始化主包 @arms/rum-browser

view、错误、资源等全部上报能力均由主包提供 —— 插件不替代主包,必须完成主包初始化,且 endpoint必填项(其余配置项与 @arms/rum-browser 一致,完整可运行示例见 examples/browser-react/src/monitor.ts):

import armsRum from '@arms/rum-browser';

// 建议放在应用入口模块执行(如 index.tsx 首行 import './monitor')
armsRum.init({
  endpoint: 'https://<你的 RUM 上报端点>', // 必填
  sessionConfig: {},
  collectors: {
    pv: { enable: true },
    jsError: { enable: true },
    // ... 其余采集器按需开启
  },
});

3. 初始化插件

initReactPlugin 可在主包 init 前后任意时序同步调用 —— 不依赖 armsRum.init() 返回的 Promise resolve,先后顺序可互换:

import { initReactPlugin } from '@arms/rum-browser-react';
import armsRum from '@arms/rum-browser';

armsRum.init({
  endpoint: 'https://<你的 RUM 上报端点>', // 必填
  // ... 其余主包配置
});
// 插件接入不再依赖 init 完成,可在 init 前后任意时序同步调用
initReactPlugin(armsRum, {
  /* router options,见下文「路由集成」 */
});

时序说明:插件接入(initReactPlugin)不再依赖主包 init 完成,可在 init 前后任意时序同步调用;init 与插件接入的先后顺序可互换,两者等价安全。路由首次导航会被内部 pending 机制暂存、init 完成后补发,不会丢失。注意:init 完成前触发的错误与自定义事件不会被采集(默认配置下 init 几乎瞬间完成;但 React 等同步渲染框架的首屏渲染期错误可能落入此窗口)。若需捕获首屏渲染期错误,可将应用挂载(mount/render)置于 init 完成后(仅挂载时序保留 .then/await,插件接入不再需要)。

4. 路由集成

插件初始化时会自动开启 trackViewsManually 手动 view 模式:view 的创建、命名、计时完全由路由变化驱动(内部调用 shell.startView),不再依赖 history 拦截。这从根本上解决了 popstate 前进/后退、首屏时序、懒加载路由、memory router 等全部时序问题。

警告:非 react-router 应用(如 Next.js)接入后 PV 不会自动采集

本插件面向 react-router(v5~v8)系应用 —— 手动 view 模式下 view/PV 完全由包装版路由 API 驱动。Next.js 等使用自带路由方案的应用接入 initReactPlugin 后:主包 PvCollector 因 trackViewsManually: true 跳过自动 PV(首屏与 history 变化均不采集),而应用内没有任何包装版路由 API 会触发 startView —— view/PV 将完全静默丢失(无报错、无告警)。

此类应用请勿直接接入本插件的路由集成,可二选一:

  • 不启用路由集成:不调用 initReactPlugin、仅使用主包 @arms/rum-browser —— 未注入 trackViewsManually 时,主包的自动 PV 采集正常工作(ErrorBoundary / useRum 等插件能力随之一并不可用);
  • 手动驱动 view:接入插件后,在首屏与页面切换时机手动调用主包实例的 startView 上报 view —— armsRum.startView('/order/:id', { loading_type: 'initial_load' }),签名 startView(name, { url?, referrer?, loading_type? })

Next.js 专用入口当前未提供。

方式一 — 使用包装后的 router API(推荐)

用包装版本替换 react-router-dom 的导入:

import { createBrowserRouter } from '@arms/rum-browser-react/react-router-v6';
// For react-router v7: import from '.../react-router-v7'
// For react-router v8: import from '.../react-router-v8'
// For react-router v5: import from '.../react-router-v5'

const router = createBrowserRouter(routes, { basename: '/app' });

initReactPlugin(armsRum);
// No need to pass routes — the wrapped APIs drive view lifecycle automatically

可用的包装 API(v6/v7/v8):createBrowserRoutercreateHashRoutercreateMemoryRouteruseRoutesRoutes

react-router v5 无 data router API(createBrowserRouter 等)与 useRoutes / Routes,包装版 API 为 BrowserRouter / HashRouter / MemoryRouter / Router / Switch —— 将 react-router-dom 中这些组件的导入替换为 @arms/rum-browser-react/react-router-v5 即可,Route / Link 等仍从 react-router-dom 导入,无需替换。

view 生命周期:

  • 首次渲染 / router 创建:loading_type: 'initial_load'
  • 后续路由变化(pushState / replaceState / popstate):loading_type: 'route_change'

URL /app/user/42view.name 将收敛为 /user/:id(而非原始路径)。

方式二 — 传入 routes(兼容保留,不推荐)

createRouterConfig 仍可传入(API 签名不变):

import { initReactPlugin } from '@arms/rum-browser-react';
import { createRouterConfig } from '@arms/rum-browser-react/react-router-v6';

const routes = [
  { path: '/', element: <Home /> },
  { path: '/user/:id', element: <User /> },
  { path: '/files/*', element: <Files /> },
];

initReactPlugin(armsRum, {
  router: createRouterConfig({ routes, basename: '/app' }),
});

注意:手动 view 模式下 view 上报由包装版路由 API 驱动。若仅使用方式二(未替换路由 API 导入),view 将不会上报 —— 请迁移到方式一:用 @arms/rum-browser-react/react-router-v6 的包装版替换 react-router-dom 的导入即可,initReactPlugin 无需传入 options.router

自检告警:插件初始化后若检测到传入了 options.router 但未使用包装版路由 API,会在 5 秒后打印一条 logger.warn 迁移提示(一次性,不拦截任何功能)。

react-router v5 支持

react-router v5([email protected]~5.3)经 react-router-v5 子路径入口支持,示例工程见 examples/browser-react-v5。包装组件为类组件实现(不依赖 5.1 引入的 hooks),兼容 react-router 5.0~5.3 全线;useRumRouteV5 除外(仅 react-router >= 5.1 可用)。

行为要点:

  • path 归一化:id?:id:id(\d+):id(对齐 v6 view.name 风格);splat 末段展开实际值,与 v6/v7/v8 行为一致(/files/* 匹配 /files/a/b → view.name 为 /files/a/b);
  • 嵌套 Switch:一次导航仅产生一条 view —— 延迟提交合并在 microtask 窗口内合并父/子 Switch 的候选,取最深模板(如 /user/:id 内嵌 Switch 声明 /user/:id/profile,仅上报 /user/:id/profile);
  • 首屏 Redirect:首屏 Redirect(REPLACE)不干扰首屏 view —— 首屏 view 收敛为 Redirect 目标模板,仅一条;
  • 懒加载(已知降级):懒加载子路由跨窗口迟到时会先提交父模板、子模板作为第二条 view(route_change)到达;同名场景由 view 同名去重兜底;
  • ErrorBoundary:在 v5 应用中行为与 v6 一致 —— React 18 下同一渲染错误出现 source='react'source='onError' 双通道为 React re-throw 固有行为,非重复上报。

ErrorBoundary

包裹你的组件树,以自动将 React 渲染错误上报至 RUM:

import { ErrorBoundary } from '@arms/rum-browser-react';

function App() {
  return (
    <ErrorBoundary
      fallback={({ error, resetError }) => (
        <div>
          <p>Something went wrong: {error.message}</p>
          <button onClick={resetError}>Retry</button>
        </div>
      )}
    >
      <UserProfile />
    </ErrorBoundary>
  );
}

addReactError

对于自定义错误边界(或 react-error-boundary 等),可手动上报错误:

import { addReactError } from '@arms/rum-browser-react';

class MyErrorBoundary extends React.Component {
  componentDidCatch(error, errorInfo) {
    addReactError(error, errorInfo);
  }
  // ...
}

上报的错误携带 source: 'react',并包含 snapshots(JSON 字符串,内部 component_stack 为 errorInfo.componentStack 组件栈、handling: 'handled')。

error 参数支持 unknown 类型(React 19 createRoot 回调中 error 可为任意值),内部自动 normalize 为 Error。

createErrorBoundary

创建自定义 ErrorBoundary 组件(工厂函数),允许接入方自定义错误上报逻辑:

import { createErrorBoundary, addReactError } from '@arms/rum-browser-react';

const MyErrorBoundary = createErrorBoundary((error, errorInfo) => {
  // 可同时上报到接入方自有的错误上报通道(可选)
  myErrorReporter.report(error);
  addReactError(error, errorInfo);
}, 'MyErrorBoundary');

<MyErrorBoundary
  fallback={({ error, resetError }) => (
    <div>
      <p>渲染出错:{error.message}</p>
      <button onClick={resetError}>重试</button>
    </div>
  )}
>
  <App />
</MyErrorBoundary>;

useRum hook

在任意组件中访问 RUM 上报能力:

import { useRum } from '@arms/rum-browser-react';

function CheckoutButton() {
  const rum = useRum();

  const onClick = () => {
    try {
      submit();
      rum.sendCustom({ type: 'biz', name: 'checkout_click', value: 1 });
    } catch (e) {
      rum.addReactError(e);
    }
  };

  return <button onClick={onClick}>Checkout</button>;
}

该 hook 返回一个稳定引用,包含以下方法:

  • addReactError(error, errorInfo?) —— 上报 React 渲染错误(error 支持 unknown,内部 normalize)
  • sendCustom(payload) —— 发送自定义事件
  • sendEvent(payload, options?) —— 发送原始事件
  • getConfig() —— 读取当前 browser 配置

API Reference

initReactPlugin(shell, options?)

  • shell —— ArmsRum 实例
  • options.router? —— 由 createRouterConfig() 生成的 RouterConfig(方式二,兼容保留,见“路由集成”)

初始化时注入 trackViewsManually: true:view 生命周期改由路由驱动(包装版路由 API 内部调用 shell.startView),PvCollector 跳过首屏 PV 与 history 拦截。

幂等:可安全地多次调用(HMR 场景)。若 react collector 已注册,将跳过重复注册;仅刷新 shell 引用与 router 配置。

包装版路由 API(方式一,推荐)

从版本子路径入口导入(@arms/rum-browser-react/react-router-v6 / -v7 / -v8),签名与对应版本 react-router API 完全一致:createBrowserRoutercreateHashRoutercreateMemoryRouteruseRoutesRoutes,以及方式二的 createRouterConfig({ routes, basename? })

react-router v5 专属导出(@arms/rum-browser-react/react-router-v5

包装组件 BrowserRouter / HashRouter / MemoryRouter / Router / Switch 的签名与 react-router-dom (v5) 完全一致(用法见「路由集成」与「react-router v5 支持」),另有:

  • createRouterConfigV5({ routes, basename? }) —— 方式二等价物(方案 C):react-router-config 风格路由表现算,供 initReactPluginoptions.router 传入;感知复用包装版 Router(BrowserRouter 等需从本入口导入);
  • useRumRouteV5(options?) —— 路由监控 hook(方案 B):在根组件调用,仅 react-router >= 5.1 可用;传入 routes 时现算模板收敛 view.name,不传时回退 URL path(无收敛能力)。推荐优先使用包装版组件。

ErrorBoundary

Props:fallback(接收 errorresetError 的组件)、children

addReactError(error, errorInfo?)

手动上报一个 React 错误。error 支持 unknown(React 19 createRoot 兼容),内部自动 normalize 为 Error。errorInfocomponentDidCatch 的第二个参数。

createErrorBoundary(reportError, displayName?)

创建自定义 ErrorBoundary 组件。reportError 为自定义上报函数,displayName 可选(React DevTools 组件名)。返回 ComponentType<ErrorBoundaryProps>

useRum()

返回 UseRumResult,包含 addReactErrorsendCustomsendEventgetConfig

computeViewName(routeMatches)

由路由匹配结果计算视图名(路由模板)—— 插件内部即用该函数将 /user/42 这类原始路径收敛为 /user/:id 模板;需要与插件口径一致地自行计算 view.name 时可直接复用。

  • routeMatches —— react-router matchRoutes() 返回的匹配数组(版本无关结构 { route, params },v5~v8 均兼容;运行时对空值/空数组安全)
  • 返回 string —— 路由模板名称,如 /user/:id/order/:orderId/detail;无匹配时返回 ''

计算规则::param 动态段原样保留(收敛的关键);嵌套路由逐段拼接(相对 path 追加,绝对 path 以 / 开头则替换已拼结果);末段 * splat 用 params['*'] 实际值展开,中间段 * 连同前导 / 移除,params['*'] 缺失时保留 *;无 path 的 layout/index 路由段跳过。

import { computeViewName } from '@arms/rum-browser-react';
import { matchRoutes } from 'react-router-dom'; // 按所用 react-router 版本导入

const matches = matchRoutes([{ path: '/user/:id' }], '/user/42');

computeViewName(matches ?? []); // => '/user/:id'

已知限制

以下限制是当前 MVP 设计所固有的,后续版本计划改进。

react-router v5:已有 view 时的 REPLACE 导航会产生新 view

v5 入口下,当会话已有 view 时执行 replace 导航(history.replace、或 v5 <Redirect> 默认 REPLACE),会产生一条新的 view(route_change)而非静默吞掉。这与“REPLACE 不应产生新 view”的直觉语义不同,用于防御首屏 Redirect 后再次 replace 场景的丢 view 问题(hasExistingViews() 时 replace 同样提交)。首屏 Redirect(尚无 view)不受影响,仍仅一条 initial_load view。

react-router v5:懒加载子路由的降级行为

懒加载子路由跨延迟合并窗口迟到时,会先提交父模板,子模板作为第二条 view(route_change)到达;同名场景由 view 同名去重兜底(见「react-router v5 支持」行为要点)。

单实例约束

该插件使用模块级单例来持有 shell/ctx 引用。在存在多个 RUM 实例或重复调用 initReactPlugin 的情况下,最后一次注册生效 —— ErrorBoundary 的错误上报可能会被路由到与预期不同的实例。未来版本计划采用 RumProvider / React Context 方案以支持多实例场景。

手动 view 模式对 PV 的影响

插件注入 trackViewsManually: true 后,browser SDK 的 PvCollector 不再自动采集 PV(首屏与 history 变化均跳过)。view 上报完全由包装版路由 API 触发(shell.startView)。这意味着:

  • 仅使用方式二(未替换路由 API 导入)时,view 不会上报 —— 请迁移到方式一;
  • 未使用本插件的其他页面/应用不受影响(trackViewsManually 由插件注入,默认关闭)。

source='react' 错误不受 collectors.jsError 采样管控

通过 ErrorBoundary / addReactError 上报的 React 错误携带 source: 'react'。browser SDK 的 SessionProcessor 采样键映射当前未包含 'react' source,这意味着这些错误会绕过 collectors.jsError 采样配置。如需禁用 React 错误上报,请使用 collectors.exception(设置为 false)。后续版本可能会在 browser session-processor 中新增 'react''js' 的 source 映射。

初始化时序

initReactPlugin 可在 armsRum.init() 前后任意时序同步调用 —— init 与插件接入的先后顺序可互换(路由首次导航经内部 pending 机制暂存、init 完成后补发,见「快速开始」的时序说明)。在初始化完成之前调用 useRum / sendCustom / addReactError 将安全降级(打印告警并跳过操作),init 完成前触发的错误与自定义事件不会被采集。useRum hook 本身可在任意时刻调用 —— 它返回一个稳定引用,在初始化完成后即可正常使用。

License

ISC