页面突然白屏?wewe-rss 前端错误监控的三层防线排障实录
2026/9/18 7:46:08 网站建设 项目流程

页面突然白屏?wewe-rss 前端错误监控的三层防线排障实录

【免费下载链接】wewe-rss🤗更优雅的微信公众号订阅方式,支持私有化部署、微信公众号RSS生成(基于微信读书)项目地址: https://gitcode.com/GitHub_Trending/we/wewe-rss

凌晨十一点,一位自建部署 wewe-rss 的朋友发来消息:公众号管理页直接白了,列表出不来,点"添加"也没反应。排查了十来分钟才发现,问题埋在请求层——认证码过期后所有请求都返回 401,而页面连一个提示都没给。这是没有前端错误监控时最典型的痛:用户说"坏了",你只能靠猜。

这篇文章就以这次事故为线索,讲清楚怎么给 wewe-rss 搭一套"三层防线 + 一个闭环"的错误监控,让下次出错时,既有出口,也有线索。

🔍 白屏的公众号页:从一个控制台报错反推问题

用户截图里,左侧订阅列表一片空白,文章区也跟着没了内容:

打开浏览器控制台,里面躺着一行红字:/trpc 请求 401。也就是说,订阅列表、文章列表的查询全挂了。更麻烦的是,页面代码里有feedData?.items ? ... : ''这类兜底,查询失败时它不会报错,而是"安静地"渲染一个空字符串——这比抛异常还难受:崩溃好歹告诉你哪里出了问题,安静失败连线索都不留。

这次事故只留下两条教训:错误必须被捕获,错误必须被用户看见。沿着这两条,我把监控拆成了三道防线。

🛡️ 第一层防线:在数据层收口 TRPC 请求失败

wewe-rss 前端用 tRPC 加 React Query 跟 NestJS 后端对话。网络错误最好的拦截点,不是在每个页面里撒 try/catch,而是在 QueryClient 里一次写死统一策略,provider/trpc.tsx 里就有现成的答案:

// apps/web/src/provider/trpc.tsx 节选 retry(failureCount, error) { if (isTRPCClientError(error) && error.data?.httpStatus === 401) { return false } return failureCount < 3 }

这几行藏着三个讲究。一是知道什么时候该停:401 意味着登录态没了,重试一百次也是 401,直接交棒给 onError,弹"无权限"提示并跳登录页。二是自动重试:瞬时的网络抖动会自己重试,最多三次。三是退避:重试间隔随次数拉长、封顶 60 秒,既不给服务器加压,也不轻易放弃。

收口的最大好处是,像 pages/feeds/ 这样的十几个页面基本不用写网络异常逻辑——错误一回来,右上角 toast 必然出现,用户不会对着空白屏幕干瞪眼。这也是给 wewe-rss 补前端错误监控时,最该先动手的第一件事。

🪂 第二层防线:给 React 渲染错误兜底

请求错误有数据层接住,那组件渲染时直接抛异常呢?比如对 undefined 读属性。这类错误 try/catch 拦不住,它会把整棵 React 组件树掀掉——这才是白屏最常见的根源。

对应工具是 ErrorBoundary,相当于组件树里的"断路器":一个挂在页面上方的类组件,任何子组件渲染时抛异常,它接住、换成兜底界面,整个应用不至于陪葬。翻 App.tsx 会发现,现在的写法是路由外面只包了 ThemeProvider 和 TrpcProvider,没有一道断路器,这是值得补的口子:

class ErrorBoundary extends React.Component { state = { error: null } static getDerivedStateFromError(error) { return { error } } render() { if (this.state.error) return <div>页面渲染出错,请刷新重试</div> return this.props.children } }

把这个类组件包在路由外层就行。顺手重写一下 componentDidCatch 记个info.componentStack,哪个组件炸的一目了然。注意断路器的管辖范围:它只管渲染阶段,点击回调和异步里的异常不归它管,那要交给第三层。

🌐 第三层防线:用两个 window 监听器补上全局安全网

断路器够不着的还剩两种:没人处理的 Promise 拒绝(点击处理器里 reject 了又没 catch),以及全局脚本错误。在入口 main.tsx 上挂两个 window 级监听,几行就够:

// apps/web/src/main.tsx 节选 window.addEventListener('error', (e) => { reportError({ type: 'global', message: e.message }) }) window.addEventListener('unhandledrejection', (e) => { reportError({ type: 'unhandledrejection', reason: e.reason }) })

reportError 就是你自己写的个小函数:收 message、stack、当前 URL、时间戳,往服务端发——最简单的做法是等下次请求成功时捎带着上报,免得再多维护一个接口。写的时候记得区分环境:env 工具里本来就有 isProd,开发环境直接弹窗最醒目,生产环境静默上报、别把敏感信息漏给用户。

🔁 从捕获到闭环:别让错误信号死在控制台里

捕到只是上半场。如果错误全进控制台、从来没人看,那跟没监控一样。处理时要先分层:401 是认证问题,引导重新登录即可,别当 bug 报;瞬时网络错误被自动重试消化了,也不必上报;真正要立刻上报的,是渲染崩溃和未处理的 Promise 拒绝。再配上频率限制——同样的错误信息 N 秒内只报一次,不然一个抖动的接口能把日志刷上天。

可以拿账号页这类交互分支最多的页面当"高危区"重点看:扫码登录、删除、状态切换,每一步都可能出错,但有了前面的防线,每种错误都有人接:

闭环的另一半在服务端。trpc.router.ts 里的 tRPC 路由早就有 try/catch 把异常转成 TRPCError,并往服务端 logger 里记了完整上下文——前端报"请求 500",后端日志里就有原始异常,两边一对照,事故定位基本结束。想动手试的话,clone 一份https://gitcode.com/GitHub_Trending/we/wewe-rss,装好依赖跑起来,改动的地方不超过三个文件。

如果只给一条建议,就是本周把这三层防线补进你的 wewe-rss:QueryClient 里收口重试与 onError,路由外包一个 ErrorBoundary,main.tsx 挂上两个 window 监听,总量半天内能搞定。下次再白屏,用户报"坏了"的时候,你能拿出的是一句准确的错误码和时间点。稳定从来不是运气,而是一层一层的兜底。

【免费下载链接】wewe-rss🤗更优雅的微信公众号订阅方式,支持私有化部署、微信公众号RSS生成(基于微信读书)项目地址: https://gitcode.com/GitHub_Trending/we/wewe-rss

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询