MST
星途 面试题库

面试题:Vue组件级懒加载在性能优化方面,怎样避免首次加载白屏问题?

当使用Vue组件级懒加载时,在某些场景下首次加载可能会出现白屏现象。请分析出现白屏的可能原因,并阐述至少两种避免首次加载白屏问题的优化策略,说明每种策略的具体实现方法。
13.9万 热度难度
前端开发Vue

知识考点

AI 面试

面试题答案

一键面试

可能原因

  1. 网络问题:懒加载组件需要在首次渲染时通过网络请求获取组件代码,如果网络状况不佳,如带宽低、延迟高,就会导致组件加载缓慢甚至长时间无法加载,从而出现白屏。
  2. 组件体积过大:懒加载的组件自身代码量庞大,包含大量的样式、脚本以及静态资源。在下载和解析这些代码时花费过多时间,导致首次加载时出现白屏等待。
  3. 路由切换触发懒加载:在路由切换过程中触发组件懒加载,若此时上一个页面的过渡动画还未结束,新组件又未及时加载完成,就会出现短暂白屏。

优化策略及实现方法

  1. 使用 loading 加载指示器
    • 实现方法:在组件懒加载时,给组件外层包裹一个 loading 状态的容器。通过 Vue 的异步组件特性,在组件加载过程中显示 loading 动画,加载完成后隐藏。
    <template>
      <div>
        <loading v-if="loading" />
        <component :is="lazyComponent" v-else />
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          loading: true,
          lazyComponent: null
        };
      },
      created() {
        this.loadComponent();
      },
      methods: {
        async loadComponent() {
          try {
            const component = await import('./LazyComponent.vue');
            this.lazyComponent = component.default;
            this.loading = false;
          } catch (error) {
            console.error('组件加载失败', error);
            this.loading = false;
          }
        }
      }
    };
    </script>
    
  2. 预加载组件
    • 实现方法:利用 router.beforeEach 钩子函数,在路由即将切换前,提前加载下一个路由对应的懒加载组件。
    import Vue from 'vue';
    import Router from 'vue-router';
    import Home from './views/Home.vue';
    
    const Foo = () => import(/* webpackChunkName: "foo" */ './views/Foo.vue');
    const Bar = () => import(/* webpackChunkName: "bar" */ './views/Bar.vue');
    
    const router = new Router({
      mode: 'history',
      routes: [
        {
          path: '/',
          name: 'home',
          component: Home
        },
        {
          path: '/foo',
          name: 'foo',
          component: Foo
        },
        {
          path: '/bar',
          name: 'bar',
          component: Bar
        }
      ]
    });
    
    router.beforeEach((to, from, next) => {
      const { components } = to.matched.reduce((acc, cur) => {
        return {
         ...acc,
          ...cur.components
        };
      }, {});
    
      const loadComponents = [];
      Object.values(components).forEach(component => {
        if (typeof component === 'function') {
          loadComponents.push(component());
        }
      });
    
      Promise.all(loadComponents).then(() => {
        next();
      }).catch(error => {
        console.error('组件预加载失败', error);
        next();
      });
    });
    
    export default router;
    
  3. 代码分割与优化
    • 实现方法:对懒加载组件的代码进行更细粒度的分割,只加载当前必要的代码部分。例如,将组件中的样式和脚本分开加载,先加载核心的渲染部分,再按需加载其他功能模块。同时,使用工具(如 webpack 的 optimization.splitChunks 配置)对代码进行优化,提取公共代码,减小单个组件的体积。
    // webpack配置示例
    module.exports = {
      optimization: {
        splitChunks: {
          chunks: 'all',
          minSize: 30000,
          minChunks: 1,
          maxAsyncRequests: 5,
          maxInitialRequests: 3,
          automaticNameDelimiter: '~',
          name: true,
          cacheGroups: {
            vendors: {
              test: /[\\/]node_modules[\\/]/,
              priority: -10
            },
            default: {
              minChunks: 2,
              priority: -20,
              reuseExistingChunk: true
            }
          }
        }
      }
    };