@arms/rum-browser-nuxt
v0.1.1
Published
Nuxt plugin for @arms/rum-browser SDK (Nuxt 3/4 file-route view tracking + dual-channel error capture, zero nuxt/vue/vue-router imports)
Readme
@arms/rum-browser-nuxt
Nuxt 插件 for @arms/rum-browser RUM SDK —— 支持 Nuxt 3 / Nuxt 4(Vue 3.5+、vue-router v4 / v5)。
能力范围:
- 文件路由风格的视图追踪:经
router.afterEach驱动Shell.startView(手动 view 模式),view.name 反向归一化为 Nuxt 文件路由风格(/user/:id()→/user/[id]),与pages/目录心智一致、聚合稳定 - Nuxt 双通道异常捕获:链式包装
vueApp.config.errorHandler(保留原 handler)+nuxtApp.hook('app:error')(Nuxt 启动 / SSR 传播错误),同一错误对象 WeakSet 去重只报一次,自动上报source: 'vue'的异常事件(snapshots内携带framework: 'nuxt') - 手动错误上报:
addNuxtError(error, context?)供onErrorCaptured/ 自定义 errorHandler 场景转发
不含:组件性能监控、hydration 错误分级、Nuxt Module 形态、Nitro 服务端集成(均为非目标,见 docs-dev/browser-nuxt-spec.md)。
安装
本插件不独立工作 —— 需与主包 @arms/rum-browser(必需 peer dependency)同时安装,并完成主包初始化:
npm install @arms/rum-browser @arms/rum-browser-nuxt发布前置条件
发布顺序(严格按序,前一步发版在 registry 生效后,再发布下一步):
@arms/rum-core@>=0.1.11:含Shell.startView与IConfiguration.trackViewsManually能力的发版(已在 npm registry 发布)@arms/rum-browser@>=0.1.16:含 PvCollector 手动模式防护(trackViewsManually早退,避免双发 PV)的发版(已在 npm registry 发布)@arms/rum-browser-nuxt(本包)
本包为独立实现(不依赖 @arms/rum-browser-vue / @arms/rum-browser-react,dependencies 为空),无兄弟包解锁约束;peerDependencies 下限与 registry 实际产物对齐,外部安装可正常解析。后续版本迭代仍需遵循上述发布顺序(依赖包发版在 registry 生效后,再发布本包)。仓库内开发期经 workspace 直链源码使用(无需等待发版),安装依赖请遵循仓库惯例使用 yarn 或 scripts/bootstrap.sh。
快速开始
在 Nuxt 项目中创建客户端插件文件(.client.ts 后缀保证仅在客户端执行):
// plugins/arms-rum.client.ts
import ArmsRum from '@arms/rum-browser';
import { initNuxtPlugin } from '@arms/rum-browser-nuxt';
export default defineNuxtPlugin({
name: 'arms-rum',
// enforce: 'pre' 保证本插件先于其他插件执行,
// 错误双通道可捕获后续插件的启动错误
enforce: 'pre',
setup() {
ArmsRum.init({
endpoint: 'https://your-endpoint',
// pid: 'your-pid',
});
initNuxtPlugin(ArmsRum, {
router: useRouter(), // 必填:驱动视图采集
nuxtApp: useNuxtApp(), // 推荐:启用错误双通道
});
},
});时序说明:
initNuxtPlugin不依赖armsRum.init()完成 —— 可在 init 前后任意时序同步调用,先后顺序可互换(反向时序经内部 pending 队列兜底,首屏 view 与排队错误不丢失)。defineNuxtPlugin/useRouter/useNuxtApp均为 Nuxt 自动导入 —— 本包源码零 nuxt / vue / vue-router 导入(SSR-safe),router / nuxtApp 由用户在setup()中取好后传入。
视图命名规则
view.name 按 to.matched 尾向头取第一个非空 path(最深匹配段即页面本身的模板),再反向归一化为 Nuxt 文件路由风格:
| vue-router 路由模板(matched.path) | view.name(Nuxt 文件路由风格) | 对应 pages/ 目录 |
| -------------------------------------- | ------------------------------ | ------------------------------ |
| /users | /users | pages/users.vue |
| /user/:id() | /user/[id] | pages/user/[id].vue |
| /blog/:slug? | /blog/[[slug]] | pages/blog/[[slug]].vue |
| /:slug(.*)* | /[...slug] | pages/[...slug].vue |
| /:pathMatch(.*)* | /[...pathMatch] | pages/[...pathMatch].vue |
| /users-:group()-:id() | /users-[group]-[id] | pages/users-[group]-[id].vue |
| 嵌套路由(父 '' + 子 /user/:id()) | /user/[id](尾向头取最深段) | pages/user/[id].vue |
静态路由原样保留;转义字符还原为字面量(\: → :)。
路由语义
警告:
router必填 —— 传入后 view/PV 完全由afterEach驱动,主包自动 PV 被禁用插件在
nuxtCollector.setup中注入trackViewsManually: true:主包 PvCollector 跳过首屏 PV 与 history 拦截(避免双发),view 的创建、命名、计时完全依赖router.afterEach驱动(内部调用shell.startView)。
导航语义(对齐 Nuxt 场景约定):
| 导航场景 | 是否开新视图 | 说明 |
| ------------------------------------ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
| 首次导航 / 首屏 | ✅ | initial_load;init 前触发经 pending 队列补发 |
| 路径变化 | ✅ | route_change(ctx 已有 view 时;否则 initial_load) |
| query 变化(path 与 hash 不变) | ❌ 跳过 | /list?page=1 → /list?page=2 不算新视图 |
| hash 变化(path 不变) | ❌ 去重 | /doc#section-a → /doc#section-b afterEach 放行但视图名不变,经 Shell.startView 同名去重后无新 view 事件(与 browser-vue 行为一致) |
| 导航失败(afterEach 第三参 failure) | ❌ 跳过 | guard 拦截 / 取消等不算新视图 |
| 同名重复导航 | ❌ 去重 | Shell.startView 内建同名去重 |
异常捕获说明
nuxtApp 传入时启用双通道自动接管:
- 通道 1:链式包装
nuxtApp.vueApp.config.errorHandler—— 先经addNuxtError上报,再调用你已有的 handler(保留 Nuxt 自带handleVueError驱动错误页的行为;无原 handler 时保持 Vue 默认的console.error可见性,不吞错) - 通道 2:
nuxtApp.hook('app:error')捕获 Nuxt 启动 / SSR 传播错误(纯 Vue 场景不存在的 Nuxt 独有通道;插件enforce: 'pre'时可捕获后续插件启动错误) - 去重:同一错误对象(引用相等)经双通道只报一次(WeakSet);非 object 错误值无引用语义,不去重
上报字段:
{
event_type: 'exception',
type: 'error',
source: 'vue', // 复用家族 vue 错误存储/查询链路
name: 'TypeError', // error.name(非 Error 值自动 normalize)
message: "Cannot read properties of undefined",
stack: '...',
// 扩展信息收敛为单一 JSON 字符串字段(值为 undefined 的内部键不会出现)
snapshots: JSON.stringify({
handling: 'handled',
framework: 'nuxt', // 本包标识,恒定携带
component_stack: 'at <OrderList>\nat <UserPage>', // 当前组件在前,沿父链向上
component_name: 'OrderList',
lifecycle_hook: 'hook:mounted', // Vue errorHandler 的 info 参数
}),
times: 1,
}也可在 onErrorCaptured / 自定义 errorHandler 中手动上报(双通道已接管时无需重复调用):
import { addNuxtError } from '@arms/rum-browser-nuxt';
// 组件内
onErrorCaptured(function (err, instance, info) {
addNuxtError(err, { lifecycle_hook: info });
return false;
});API
主入口 @arms/rum-browser-nuxt(单入口,无子入口)
| 导出 | 说明 |
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| initNuxtPlugin(shell: Shell, options: NuxtPluginOptions) | 初始化插件(collector 注册 + 路由视图采集 + 条件启用错误双通道) |
| addNuxtError(error: unknown, context?: NuxtErrorContext) | 手动上报 Nuxt 异常(component_stack / component_name / lifecycle_hook 可选) |
| type NuxtPluginOptions / NuxtErrorContext 等 | 公共类型(duck typing,版本无关) |
NuxtPluginOptions
initNuxtPlugin(shell, options) 的第二参数(源 src/types/index.ts):
| 字段 | 类型 | 必填 | 说明 |
| --------- | ------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| router | AnyRouter | 是 | vue-router 实例(v4 / v5 均可,useRouter() 取得,仅依赖 afterEach / currentRoute 最小接口)。驱动 view 生命周期(手动 view 模式:注入 trackViewsManually + 调用 Shell.startView) |
| nuxtApp | NuxtAppLike | 否 | NuxtApp 实例(useNuxtApp() 取得)。提供时启用错误双通道自动接管;不提供时仅启用路由视图采集,错误可经 addNuxtError 手动上报 |
NuxtErrorContext
addNuxtError 的第二参数(字段对齐 @arms/rum-browser-vue 的 VueErrorContext):
| 字段 | 类型 | 说明 |
| ----------------- | -------- | ----------------------------------------------------------------------------------- |
| component_stack | string | 组件链栈,如 'at <OrderList>\nat <UserPage>'(当前组件在前) |
| component_name | string | 出错组件名 |
| lifecycle_hook | string | 生命周期钩子 / 错误来源信息(Vue errorHandler 的 info 参数,如 'hook:mounted') |
版本要求
| 依赖 | 版本要求 | 说明 |
| ------------------- | -------------------- | ------------------------------------------------------------------------------------------------ |
| @arms/rum-core | >=0.1.11 | 含 Shell.startView 与 IConfiguration.trackViewsManually(已发布) |
| @arms/rum-browser | >=0.1.16 | 含 PvCollector 手动模式防护(已发布) |
| nuxt | 3(>=3.7)\|\| 4 | optional peer;3.7 起 defineNuxtPlugin 对象语法与 enforce 稳定 |
| vue | ^3.5.0 | optional peer;本包经 nuxtApp.vueApp.config.errorHandler duck typing 访问,不直接依赖 vue 模块 |
| vue-router | ^4.0.0 \|\| ^5.0.0 | optional peer;仅依赖 afterEach / currentRoute 最小接口 |
版本口径说明:peerDependencies 中
nuxt声明为3 || 4,实际支持基线为 Nuxt ≥3.7 —— 更早版本的defineNuxtPlugin对象语法与enforce不稳定。vuepeer 为^3.5.0,而 Nuxt 3.7-3.12 生态常配 vue 3.3 / 3.4 —— 三项框架 peer 均 optional(peerDependenciesMeta标记),版本不满足仅产生安装告警、不阻塞安装(插件对框架实例 duck typing,实际兼容宽于声明口径)。
Nuxt 2 用户
Nuxt 2 / Vue 2 已 EOL(插件体系完全不同),本包不支持。请使用通用包 @arms/rum-browser(自动 PV + 全局异常采集,无 Nuxt 专属能力),或升级到 Nuxt 3 后接入本包。
已知限制
以下限制是当前 MVP 设计所固有的,后续版本计划改进。
单实例约束
插件为 collector 级单例(模块级状态持有 shell / ctx 引用)。重复调用 initNuxtPlugin 传入另一个 rum 实例时会打印 logger.warn 并跳过重复注册、仅刷新 shell 引用 —— 最后一次绑定的实例生效。微前端多子应用各自持有独立 RUM 实例的场景暂不支持。
source='vue' 错误不受 collectors.jsError 采样管控
本包上报的错误携带 source: 'vue',browser SDK 的 SessionProcessor 采样键映射当前不覆盖 'vue'(与 browser-vue 包同款已知约束)——这些错误会绕过 collectors.jsError 采样配置。如需禁用,请使用 collectors.exception(设置为 false)。另外,core reporter 对 exception 按 getErrorID(message + stack) 聚合且 key 不含 source —— 同一错误被全局通道与插件双通道捕获时会聚合合并;自动通道错误可经 filters.exception 过滤以保留富上下文事件。
errorHandler 覆盖边界
- 组件
onErrorCaptured返回false:按 Vue 语义该错误被视为"已处理"并阻止传播,全局errorHandler不会触发 —— 插件捕获不到(Vue 框架行为);需观测时请在onErrorCaptured中手动调用addNuxtError; warnHandler不覆盖:Vue 运行时警告不进入 RUM 异常事件;unhandledrejection:由主包@arms/rum-browser的 jsError 采集器覆盖,无需本插件处理;- hydration mismatch 明细:Vue errorHandler 不捕获(vuejs/core#13154),不做监控承诺。
错误页为全新页面加载
Nuxt 渲染 error.vue 属全新页面加载,插件与会话上下文随之重置 —— 错误后首屏 view 重新计为 initial_load。
无 pages/ 目录的应用
仅单 app.vue 的 Nuxt 应用没有路由实例,插件因缺少有效 router 整体禁用(logger.error 自监控上报),仅 addNuxtError 手动错误上报可用。
错误去重窗口语义
双通道去重按 app 生命周期(WeakSet 按错误对象引用)—— 同一 Error 实例二次抛出会被去重丢弃。
初始化时序
initNuxtPlugin 可在 armsRum.init() 前后任意时序同步调用。初始化完成之前调用 addNuxtError 时安全降级:插件未接入则 warn + skip(不抛错);插件已接入但 init 未完成则排队,init 完成后自动补发。init 前触发的路由导航经 pending 队列兜底,collector setup 后自动补发首个 view(loading_type: 'initial_load'),首屏不丢失。
兼容性
| 环境 | 支持情况 | 说明 |
| --------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Vite / webpack 5 等 | ✅ | 经 exports 字段解析单入口(types / import / require / default 四条件) |
| SSR(Nuxt 服务端) | ✅ 安全 | 源码零 nuxt / vue / vue-router 导入、零顶层 window / document 访问(唯一 DOM 触点 getCurrentUrl 在函数内 try-catch 防护);配合 .client.ts 后缀约定使用 |
| CDN / <script> 标签 | ❌ 不适用 | 本包不出 UMD dist 产物,仅经 bundler 消费 —— CDN 场景请直接使用主包 @arms/rum-browser |
产物压缩说明:
es/lib产物已经过 terser 压缩与混淆,且不附带 sourcemap(与 browser-vue / browser-nextjs 包一致的体积/混淆 trade-off)。排查线上堆栈时请对照仓库源码与 CHANGELOG 版本定位对应发布版本。
