必须同时监听 submit 事件和网络请求生命周期才能准确统计表单提交耗时,仅监听 submit 只能获取发起时间,无法覆盖网络请求、后端处理、重定向及 SPA 跳转等完整链路。

为什么不能只监听 submit 事件就完事
表单提交耗时不是从 click 按钮开始算的,而是从用户触发提交动作(比如点按钮、按回车)到服务器响应完成(fetch 或 XMLHttpRequest 的 onload 或 then)的完整链路。只监听 submit 事件只能拿到“发起时间”,但无法捕获网络请求阶段、后端处理、重定向、甚至页面跳转后的 onload 时间——尤其在 SPA 场景下,submit 后可能只是发个 fetch,页面不刷新,你若没拦截请求,就彻底丢失耗时。
常见错误现象:
- 页面跳转后才上报,但跳转前的请求已超时,日志里看不到失败
- 表单用
event.preventDefault()+fetch提交,却没给fetch打点,上报耗时恒为 0 - 多个表单共用同一套 JS 提交逻辑,但埋点字段(如
form_id)没随上下文动态传入,所有耗时都归到同一个 ID 下
如何准确打点:封装 submit + 拦截 fetch/XHR
必须同时覆盖两个层面:DOM 层的提交触发点,和网络层的实际请求生命周期。
- 对每个
form元素绑定submit事件,在event.preventDefault()后立即记录起始时间戳:performance.now() - 若表单走原生提交(
action+method),需监听window.addEventListener('beforeunload', ...)并配合navigationStart和loadEventEnd计算跨页耗时(仅限非 SPA) - 若走 JS 提交(推荐),统一调用封装函数
submitForm(formEl, options),内部自动:- 记录
start: performance.now() - 发起
fetch或XMLHttpRequest,并在then/catch或onload/onerror中记录结束时间 - 补全业务字段:
form_id(取自formEl.id或formEl.dataset.formId)、submit_method(POST/PUT)、is_ajax
- 记录
- 必须处理
fetch被 reject 的情况(如网络中断、CORS 失败),否则这类失败请求完全不会进入then/catch,导致监控缺失
容易漏掉的关键细节
- 表单内按钮可能是
type="button" 而非 type="submit",靠监听 submit 事件会漏掉;应同时监听带 data-submit-trigger 的点击事件
- 用户连续快速点击两次提交按钮,若没加防重复(如按钮置灰 +
AbortController),会发出两个请求,但埋点可能只记录第一个的 start,第二个的 end,造成负耗时或数据错乱
-
fetch 默认不带凭据(credentials: 'include'),若后端依赖 session,请求会 401,但前端可能只当“慢”处理,实际是鉴权失败——建议在上报字段中加 http_status 和 error_type(区分 network / timeout / auth)
- SPA 中路由切换(如
router.push('/success'))发生在请求成功后,但用户感知的“提交完成”其实是跳转后,此时应把跳转耗时也纳入统计(用 performance.getEntriesByType('navigation') 取最近一次 navigation 的 domContentLoadedEventEnd - fetchStart)
上报时机与字段设计
type="button" 而非 type="submit",靠监听 submit 事件会漏掉;应同时监听带 data-submit-trigger 的点击事件AbortController),会发出两个请求,但埋点可能只记录第一个的 start,第二个的 end,造成负耗时或数据错乱fetch 默认不带凭据(credentials: 'include'),若后端依赖 session,请求会 401,但前端可能只当“慢”处理,实际是鉴权失败——建议在上报字段中加 http_status 和 error_type(区分 network / timeout / auth)router.push('/success'))发生在请求成功后,但用户感知的“提交完成”其实是跳转后,此时应把跳转耗时也纳入统计(用 performance.getEntriesByType('navigation') 取最近一次 navigation 的 domContentLoadedEventEnd - fetchStart)别等页面卸载再发日志,sendBeacon 是底线,但更稳妥的是:成功/失败后立刻 fetch(..., { method: 'POST', keepalive: true }) 上报;失败且无网络时,缓存到 localStorage,下次页面加载时补发。
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
必报字段示例:
-
form_id: 唯一标识表单(避免用 class 名,易冲突) -
start_ts/end_ts:performance.now()时间戳,单位毫秒 -
duration:end_ts - start_ts,后端校验是否为正数 -
http_status: 仅限 JS 提交,取自response.status或xhr.status -
error_type: 如"timeout"、"aborted"、"network" -
is_retry: 是否为重试请求(根据AbortController.signal.aborted或重试计数判断)
跨表单提交耗时监控最难的不是采集,而是把「用户意图」、「DOM 触发」、「网络执行」、「前端反馈」四段耗时对齐并归因。多数问题出在字段没对齐、异常路径没覆盖、或误把页面跳转时间当成请求时间。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










