白屏根因分析&检测

先有问题再有答案

  1. 白屏现象的本质是什么
  2. 有什么危害
  3. 白屏是如何产生的
  4. 如何监控到白屏
  5. 白屏如何修复

白屏的影响

  1. 如果线上发生白屏故障 页面pv uv 点击量 业务交易量等各种指标会瞬间迭0 业务系统全面瘫痪 毫无疑问这个是p0故障...
  2. 品牌形象严重受损:频繁或长时间的白屏问题可能影响用户对品牌的整体看法。它可能被视为技术不可靠或服务不专业的标志,对品牌形象和声誉造成长期损害。

关键链路

首先看下用户访问H5页面的关键链路: 用户从点击入口到看到内容依次会经历上面的几个关键节点。

下载 (Loading):

这里主要是网络层面的事情例如dns,tcp,http握手等获取到html,js,css,图片,音视频的内容资源文件。

解析 (Parsing):

  • HTML解析:浏览器开始解析HTML文档,构建DOM(文档对象模型)树。这个阶段是将标记语言转换成浏览器能理解和操作的结构化数据。
  • CSS解析:同时,CSS文件被解析成CSSOM(CSS对象模型)。
  • JS解析:JavaScript代码被解析并编译。浏览器会解析JavaScript脚本,可能暂停HTML的解析,这是因为JavaScript可能需要修改DOM结构。

执行(Execution)

执行已解析的JavaScript代码,JavaScript可能会操作DOM和CSSOM,注册事件处理函数,以及调用Web API等。

API请求(API Requests)

页面上的JavaScript往往会发起API请求,获取服务器端的数据。这些数据请求通常通过XMLHttpRequest或Fetch API进行。

渲染数据(Render Data)

使用从API获取的数据,JavaScript会更新DOM结构,如插入数据、修改元素等操作。DOM的更新可能导致页面布局的更改(重排)和元素外观的更改(重绘)

交互状态(Interactive)

此时页面已经完全加载,所有的事件监听器都已准备就绪,用户可以与页面进行交互,例如点击按钮、提交表单等。

白屏的根因

当上面的某个环节耗时较长或者发生异常阻塞后面的流程 导致用户看不到有意义的内容即发生白屏。所以白屏是渲染的过渡或者渲染异常的表现。

下载:

  • 网络延迟或中断:网络问题可能导致资源文件未完全加载。
  • 服务器故障:如果服务器遇到故障(例如,服务器宕机、配置错误等),可能无法正确响应请求。
  • 资源找不到(404错误):请求的资源文件(如JavaScript或CSS文件)在服务器上不存在。
  • cdn异常:使用的cdn服务不可用异常宕机 导致用户无法加载到资源

解析:

例如在低版本浏览器中使用es6+的语法, 引擎解析阶段无法识别一些符号 导致js解析异常 影响了后续流程, 引起页面白屏。

执行:

执行阶段因为开发编写的逻辑异常&没有做好兜底兼容导致白屏。例如首屏接口的处理没有try catch.

渲染:

一般UI框架(react/vue)都会有一些兼容处理避免因为渲染异常导致白屏,但是在读取一些动态数据 无法获取的时候,依然会引起渲染异常导致页面白屏。

例如我们在vue框架中,在模板中使用a.b.c取值时,可能会因为业务逻辑导致a||b不存在发生渲染报错。如果报错发生在根组件 则会导致页面白屏

如果报错发生在页面的子组件 则会导致子组件无法渲染 业务部分功能不可用

白屏的处理

  1. 网络层面的问题一般与业务无关 这个环节我们需要做的是完善监控和报警 可以及时发现异常
  2. 解析的兼容性问题 我们需要统一业务的目标版本,对目标环境通过babel做好polyfill.
  3. 执行时做好代码的cr和风险评估 在关键节点做好异常兜底。
  4. 渲染环节要充分利用好框架的errorboundry能力,渲染兜底页面同时上报错误栈。

白屏检测方案:

监控异常

这个是利用onerror的机制 监听页面内的关键资源是否发生了加载异常。或者监控js运行报错是否会导致页面崩溃....

dom树检测

  • 根节点判断

    通过在页面底部插入一个脚本读取dom节点,判断dom是否发生变化,因为一般首屏的script标签是顺序同步执行的,在执行最后的脚本时,前面的基础js和业务js应该都已经下载解析执行完成了dom也已经发生了变化,所以这个阶段如果dom依然和初始化是一致的说明发生了异常,页面白屏了

  • 采样对比

    原理就是从页面中取出一些关键dom或者按照一定的逻辑均分页面。然后轮训一定次数 判断获取的节点是否为容器节点或者判断获取的节点宽高是否为0.
    H5的白屏检测方案实践
    前端白屏的检测方案,让你知道自己的页面白了

框架兜底

正如前面分析的一样 在渲染阶段 白屏本质是由于错误 导致框架不知道怎么渲染,所以干脆就不渲染。因此我们要充分利用好框架给我们提供的能力,毕竟在这个spa框架下 我们已经将dom托管给了vue/react了。

下面是利用vue的error-boundary实现的能力:

xml 复制代码
<template>
    <div :class="className">
        <slot v-if="PageStatus.init === status"></slot>
        <template v-if="PageStatus.error === status">
            <slot v-if="showDefault" name="error"></slot>
            <div v-if="!showDefault" class="content">
                <p class="text">页面渲染异常</p>
                <div class="btn" @click="onRefresh">刷新试试</div>
            </div>
        </template>
    </div>
</template>

<script lang="ts">
import { defineComponent, onErrorCaptured, PropType, ref } from '@vue/composition-api';
import { IError, PageStatus } from './type';

const componentName = `vue-error-boundary`;

export default defineComponent({
    name: componentName,
    components: {},
    props: {
        /**
         * targetId
         * 被监控的目标组件name 输入空字符匹配所有
         * 目标组件一般为业务根组件
         */
        targetId: {
            type: Array as PropType<Array<string>>,
            default: [],
        },
        /**
         * onRenderErrorTarget
         * 监控的目标组件报错 目标组件一般为业务根组件
         * !! 这里报错意味着业务已经完全不可用
         */
        onRenderErrorTarget: {
            type: Function as PropType<(params: IError) => void>,
            default: () => ({}),
        },
        /**
         * onRenderError
         * 子组件有渲染报错
         * !! 这里报错意味着业务部分不可用
         */
        onRenderError: {
            type: Function as PropType<(params: IError) => void>,
            default: () => ({}),
        },
        /**
         * onError
         * 捕获全部异常 包括js运行异常 渲染异常 生命周期执行异常
         * !! 通常用于上报雷达发送自定义埋点
         * 可以收集到子组件的全部报错
         */
        onError: {
            type: Function as PropType<(params: IError) => void>,
            default: () => ({}),
        },
    },
    setup(p, ctx) {
        const showDefault = ref(!!ctx.slots.error);
        const status = ref(PageStatus.init);
        const { onRenderError, targetId, onRenderErrorTarget, onError } = p;
        onErrorCaptured((err: any, vm: any, info: string) => {
            try {
                const params = {
                    component: vm,
                    err,
                    type: info,
                    tag: vm.$vnode.tag,
                    propkeys: Object.keys(vm.$vnode.componentOptions?.propsData || {}),
                    instanceKeys: Object.keys(vm.$vnode.componentInstance || {}).filter(
                        item => item[0] !== '_' && item[0] !== '$',
                    ),
                };
                if (info === 'render') {
                    if (targetId.some(item => `${vm.$vnode.tag}`.endsWith(`${item}`))) {
                        status.value = PageStatus.error;
                        onRenderErrorTarget(params);
                    } else {
                        onRenderError(params);
                    }
                }
                onError(params);
            } catch (error) {
                onError({
                    err: error
                });
            }
        });

        const onRefresh = () => {
            location.reload();
        };
        return {
            showDefault,
            status,
            PageStatus,
            onRefresh,
            className: componentName,
        };
    },
});
</script>
相关推荐
崔庆才丨静觅12 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606113 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了13 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅13 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅14 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅14 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment14 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅14 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊14 小时前
jwt介绍
前端
爱敲代码的小鱼14 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax