@gantfdt/fp-umi-plugin
v0.4.2
Published
umi plugin that wires @gantfdt/fp-sdk into a umi app: entry injection, React Profiler root container, dev-server /fp-viewer/ static hosting
Maintainers
Keywords
Readme
@gantfdt/fp-umi-plugin
把 @gantfdt/fp-sdk 性能采集接入 umi 应用的插件。宿主只需安装本包并在 umi config 注册,无需直接依赖 @gantfdt/fp-sdk。
安装与注册
pnpm add @gantfdt/fp-umi-plugin -D # npmjs 公开包,默认 registry / npmmirror 直接可装// framework/config/config.ts
plugins: [
// ...已有插件
require.resolve('@gantfdt/fp-umi-plugin'),
],
fpPerf: {
appKey: 'bom-web', // 缺省取宿主 package.json name(多应用共用包名时必须显式)
// collectUrl: '/integration/perfCollect/ingest.api', // 默认值,dev 与生产同形(网关按前缀路由)
// configUrl: '/integration/perfConfig/findConfig.api', // 默认值,显式 '' 关闭远端采样
// compress: true, // gzip 上报;后端未支持 gzip 请求解码时必须显式 false
// panel: true, // 取证面板入口(默认 true);false 时完全零副作用
// panelUrl: '/fp-viewer/panel.js',// 面板脚本地址默认值,与 /fp-viewer/ 托管同路径
// traceRate / slowTraceMs / flushIntervalMs 均有默认值
// profiling: true, // 取证构建才开(默认关):react-dom 切 profiling 产物,详见下节
// autoProfile: { include: ['src/pages/**'] }, // 组件级自动包装(默认关),依赖 profiling 开启;
// // 业务源码在宿主 cwd 之外时须设 cwd(见「组件级归因」节)
},门禁与开关
总门禁(production-only,fail-closed):仅 production build(api.env === 'production')插件才可能
生效;umi dev / test / 环境识别不明一律按 dev 处理,无论下列开关如何配置都注册后立即返回、零副作用
(不注入 entry/runtime、不挂中间件、不动 webpack/babel 配置、不写文件——宁可不采集,也不影响同事 HMR)。
生产构建内再由开关决定是否注入:
FP=1环境变量,或fpPerf: { enabled: true }(构建期固化,生产接入用)
普通生产构建未显式开启同样零注入,产物与基线零增量。
开启后发生什么
- entry 最前面注入
init.js → initFpPerf(sdkConfig)(config 在构建期内联); - 注册 runtime 插件:React Profiler 包裹根节点(生产 React 不触发回调,透明传递);
- 宿主若安装了
@gantfdt/fp-viewer:注册/fp-viewer/静态中间件,把 viewer 的 dist 以同源 子路径托管(viewerPath可改路径,viewerStatic: false可关闭)。addBeforeMiddlewares仅 dev server 生效——总门禁下 dev 恒短路,此注册在显式开启的生产构建中只是惰性 no-op、不进产物, 面板静态资源由宿主构建产物承接(见「viewer 面板」节)。
URL 分类(fpPerf 配置)
umi dev server 的 HMR 通道是 SockJS 长连接(断线自动重连),采集它会占住 trace 在途名额、
把所有 trace 强制截断——插件默认内置忽略 /dev-server/、/sockjs-node/、.hot-update.,无需配置。
ignoreUrls: string[]:追加不采集的 URL(子串匹配),与内置默认合并;streamingUrls: string[]:SSE/流式接口(如['/bommgmtsse/'])——照常采集(瀑布图可见, 事件带streaming:true标记)但不占 trace 在途名额,长连接不再把 trace 挂到强制截断。
注意:sdkConfig 经 JSON 内联进 entry,此处只支持字符串;需要 RegExp 时请直接用 @gantfdt/fp-sdk 的 initFpPerf。
viewer 面板(仪表盘)接入
宿主安装 @gantfdt/fp-viewer 后,面板即可经 iframe 同源嵌入:
<iframe src="/fp-viewer/" />viewer 的接口 baseURL 固定为同源 /integration(接口路径带 .api 后缀,经网关按前缀路由;perfmgmt 已并入 integration-app,2026-09-20 与后端对齐不设独立前缀),生产走聚合网关。
生产部署:把 node_modules/@gantfdt/fp-viewer/dist 拷入宿主构建产物的同名子路径(如
dist/fp-viewer/),由宿主既有静态托管(nginx/OSS/CDN)承接,无需动后端与网关路由。
取证面板(Runtime Forensics 阶段一)
sdk 内置面板入口 stub(默认开启,未激活零副作用)。三入口任一命中即激活:
- 快捷键
option+shift+P(按e.code === 'KeyP'判定,绕开 macOS option 组合键的 key 变体); - URL 参数
?__fp_debug=1; localStorage.__fp_panel = "1"。
激活后按需注入 panelUrl 脚本——自包含 classic script(React 18 内联、Shadow DOM 自挂载、暴露
window.__fpPanel),加载失败仅 console.warn,不影响宿主页面。
前提:宿主升级的 @gantfdt/fp-viewer 版本 dist 含 panel.js。/fp-viewer/panel.js 由宿主
静态产物承接(与 viewer dist 同包拷入、同路径,见上一节)。
panel: false:关闭入口 stub(三入口均不再注入脚本);panelUrl: '/自定义路径/panel.js':面板脚本托管在别处时覆盖默认地址(空串视为未配置)。
组件级归因:profiling / autoProfile(默认均关)
普通生产构建的 react-dom 不触发 Profiler.onRender 回调,组件级归因(面板「组件分析」区)
需要显式开启 profiling 构建。设计规格见 docs/PROFILER_AUTOWRAP.md。
fpPerf: {
enabled: true,
profiling: true, // 显式开启(默认关)
autoProfile: { include: ['src/pages/**'] }, // 构建期自动给业务组件 export 包 Profiler 边界
},profiling: boolean(默认false,必须显式开启):chainWebpack 注入react-dom$ → react-dom/profilingalias(用 createRequire 从宿主 cwd 解析,指向宿主自身 node_modules,不经过插件依赖树)。React 16/17 宿主会成对注入scheduler/tracing$ → scheduler/tracing-profiling——缺它 Profiler commit 路径抛 React 错误 #302 直接白屏(tracing 子系统 React 18 才移除,18 只需单 alias)。宿主 react 版本探测 或 profiling 产物解析失败时不注入任何 alias 并console.warn(fail-open:半套 alias 比不注入更危险)。 注入时若检测到 umi 内置前缀 alias(react-dom → <cwd>/node_modules/react-dom,preset-built-in 默认注入),会先摘下、注完精确键再挂回末尾——webpack4 对象 alias 按键序首个匹配生效,不重排 则react-dom$被前缀键遮蔽永不命中(scheduler/tracing同理);重排中途失败会回滚为注入前 状态(原 alias 不丢)并 warn。autoProfile: { cwd?: string, include?: string[], exclude?: string[] } | false(缺省关):经modifyBabelOpts注入@gantfdt/fp-babel-plugin-profiler,构建期自动包装业务组件 export,并在输出目录产出fp-components.jsonmanifest(组件 id → 文件:行)。manifest 走 sidecar 汇合 + 跨构建快照(2026-10-25 起):各 babel 进程只落自己本次实际转换的切片(原子 rename 写$TMPDIR/fp-profiler-manifest-<hash>/worker-<pid>.json),本插件注册时清被杀构建的 残留 worker sidecar(保留快照)。onBuildComplete先以deferWarnings: true临时合并全部已就绪 切片 ∪ 跨构建快照(<cwd>/node_modules/.cache/fp-profiler/manifest-*.json, configHash(cwd/include/exclude) 匹配才承接、快照条目按 existsSync 修剪已删文件)并落盘 (组件并集、确定性排序),但此时没有 sidecar/manifest 仍属合法中间状态,不提前告警;主进程 exit 兜底在 babel sidecar 写入后做最终合并,并只在此时按三态判定覆盖告警——babel-loader 磁盘缓存命中的文件不经插件、无 sidecar,其包装记录由快照承接,暖缓存构建 manifest 不再缺席; babel 插件版本注入 options(缓存 key 含 options JSON):插件升版本即自动全量重转,杜绝宿主 静默重放旧版转换结果。include缺省['src/**'],exclude缺省['**/*.test.*', '**/*.spec.*', '**/node_modules/**'](与 babel 插件缺省对齐)。cwd缺省宿主 umi cwd,是include/exclude匹配的相对路径基准: monorepo 下业务源码在宿主 cwd 之外(如 umi 跑在framework/、页面在../packages/*)时必须 显式设为共同祖先目录,否则 babel 插件对path.relative(cwd, file)以../开头的文件直接跳过,业务页面永远包不到。cwd支持相对路径——以 umi cwd(如framework/)为 基准 resolve 成绝对路径再传给 babel 插件,因此cwd: '..'(仓库根)这类写法可跨机器/CI 移植; 绝对路径原样透传。fp-components.json的完整性不再依赖冷构建(暖缓存由快照承接), 但首次构建(或快照被清)仍需全量 transform 才有完整快照——autoProfile.cwd/include变更本身会改变 babel options、自动触发全量重转。autoProfile依赖profiling:包装产物在非 profiling 的 react-dom 上拿不到 Profiler 回调,注入也是白注入——autoProfile开而profiling未显式开时,插件拒绝注入 babel 插件 并console.warn,需同时设置profiling: true。- 两者缺省均关:未开启时插件不注册任何 chainWebpack / modifyBabelOpts 逻辑,
普通生产构建产物与基线零增量(可 diff 证明)。SDK 未初始化时
fpProfile运行时 fail-open 原样返回组件,已注入产物在 SDK 缺失时行为与未包装一致。
生产接入形状
enabled: true + collectUrl 指向网关承接的上报地址;/fp-viewer/ 静态资源由拷入宿主产物的静态文件承接,中间件机制不进产物。
pnpm 说明
init.js/runtime.js 对 @gantfdt/fp-sdk、@gantfdt/fp-runtime-react、react 的引用从插件自身依赖树解析
(react 为 peer,pnpm 自动链接宿主版本,保证单例)。宿主不需要直接安装 @gantfdt/* 其他包。
