返回

Vuetify样式延迟加载:原因分析与四种优化方案

vue.js

Vuetify 样式延迟加载问题剖析

使用 Vuetify 框架进行 Web 开发时,开发者有时会遇到页面初始加载时样式闪烁,元素样式错乱,随即恢复正常的现象。 这主要是由于 Vuetify 的 CSS 样式在初始 DOM 加载之后才被添加导致的。 该现象影响用户体验,必须妥善处理。本文将深入分析此问题根源并提供多种解决方案。

问题成因

问题的根本在于浏览器解析渲染页面 HTML 结构的速度快于 Vuetify CSS 的加载速度。特别是在服务器端渲染(SSR)的环境下,页面首先在服务端生成静态 HTML,传递到浏览器。而 CSS 依赖客户端渲染,Vue组件的Vuetify样式需要等待Vue加载完成才会动态加载,这时就会导致样式表应用出现滞后,造成页面初次加载时的“闪烁”或者布局错乱。 浏览器会首先按照无样式渲染页面结构,待 Vuetify CSS 载入后,重新渲染,从而出现了短暂的不良观感。

解决方案

下面列出几个可尝试的解决方案,以及实现步骤和相应的代码。

方案一:利用服务端渲染注入 CSS

使用 SSR 时,可以在服务器端预先渲染并注入必要的 Vuetify 样式,这样在浏览器端就可以立即使用这些样式。

  • 操作步骤:

    1. 找到 Nuxt 的 nuxt.config.js 文件。
    2. 修改 vuetify 模块配置。
  • 代码示例:

// nuxt.config.js
module.exports = {
  buildModules: [
    '@nuxtjs/vuetify'
  ],
  vuetify: {
     options: {
      customVariables: ['~/assets/variables.scss'],
    },
    treeShake: true, // 若已经存在请忽略
     //以下是添加的代码
    ssr: true
  },
};
  • ssr: true 配置会指示 vuetify 模块在服务端渲染期间注入 CSS。

  • customVariables允许使用自定义的Sass文件进行 Vuetify 配置,你可以自定义变量和样式来满足特定的项目需求,确保样式匹配应用程序的视觉规范,且保持组件的整体视觉一致性。

  • 说明:
    这方法使Vuetify 样式与初始 HTML 一起传输,避免了客户端渲染时样式加载延迟问题。 注意该方案需要在 SSR 环境下才能起作用。

方案二:预加载 Vuetify 样式

利用 <link rel="preload"> 指令可以预加载 Vuetify CSS 文件,浏览器将在后台下载这些资源,并在需要时快速使用,从而缩短样式加载的时间差。

  • 操作步骤:

    1. 在 Nuxt 的 nuxt.config.js 文件中修改 head 选项。
  • 代码示例:

// nuxt.config.js
module.exports = {
  head: {
    link: [
       // 注意替换为正确的样式文件路径。 比如 `~/assets/styles/vuetify.min.css`
       // 或者 根据Vuetify 打包后的最终CSS路径来确定。
      { rel: 'preload', href: 'path/to/vuetify.min.css', as: 'style' },
       { rel: 'stylesheet', href: 'path/to/vuetify.min.css' }
    ]
  }
};
  • 需要注意 CSS 文件路径是否正确,路径不匹配会使该方案失效。可以使用console.log来验证。
  • 说明:
    该方法利用预加载指令优化加载资源的时机,一定程度上减少样式闪烁。preload 配合 stylesheet,确保预加载完成的同时正确应用样式。

方案三: 内联关键 CSS

对于关键的,在首次渲染必须展示的内容,可以使用内联 CSS 将少量 Vuetify 样式直接嵌入 HTML 中。

  • 操作步骤:

    1. 使用工具(如 critters)分析关键渲染路径。
    2. 提取相关的 Vuetify 样式。
    3. 修改 nuxt.config.js 文件中的 head 选项,把关键样式添加至style 标签中。
  • 代码示例:
    首先需要安装 critters

 npm i critters

然后在 nuxt.config.js 中配置

// nuxt.config.js
const { Critters } = require('critters');

module.exports = {
    head: {
        __dangerouslyDisableSanitizersByTagID: {
            'critical-styles': ['innerHTML'],
          },
        
    },
    render: {
        bundleRenderer: {
            runInNewContext: false,
            
        },
       async ssrPrefetch() {
        const renderer = await this.getBundleRenderer();

        let  style = '';
         //创建Critters实例
        const critters = new Critters({
            path: 'static'
         })

        // 这里做一次渲染,然后提取内联的css,可以添加页面限制(如 only critical-paths)
        await this.renderRoute('/')
        .then((html)=>{
            style =  critters.process(html).toString()
        })
        // 可以添加 try/catch 处理异步操作错误。

         this.$head.style.push({
               hid: 'critical-styles', // 指定唯一的 id 防止被覆盖
            innerHTML: style
           });
       
      },

     },
    
}
  • 说明:
    该方法可以避免初始化渲染无样式的问题。 内联样式虽然会增加 HTML 文件体积,但对于少量关键样式是值得的,该方法需要在服务端预处理,可以考虑加缓存提升处理速度。 需要配合critters之类的工具才能自动化完成关键路径 css 提取,过程较为复杂,不建议直接人工提取样式。

方案四:使用 v-cloak 指令

v-cloak 指令可以和 CSS 配合使用,使得在 Vue 实例挂载之前,不会显示未编译的组件内容。

  • 操作步骤:

    1. 在 Vuetify 组件外围元素加上 v-cloak 指令。
    2. 在全局样式文件中添加 [v-cloak] 的样式定义,通常设为隐藏。
  • 代码示例:

// 页面模板
<template>
  <div v-cloak>
    <v-app>
        <v-navigation-drawer></v-navigation-drawer>
        <v-main></v-main>
   </v-app>
  </div>
</template>
/* 全局样式表文件 */
[v-cloak] {
  display: none;
}
  • 说明:
    该方法防止未编译内容闪烁,待 Vue 完成编译和 Vuetify 加载之后再显示元素,实现初始页面平滑过渡。这方案只能解决部分“闪烁”问题,不能完全避免样式延迟加载带来的问题。

安全提示

  • 请勿直接从 CDN 或外部链接引用 CSS 资源,避免造成加载缓慢甚至网络风险。
  • 进行代码修改前,请备份配置并进行充分测试。
  • 使用代码混淆工具时需要小心配置,以免干扰样式表的加载。

以上列出的解决方案各有侧重,可根据项目的具体情况选用合适的策略。 若组合使用,效果更佳。 例如:服务端渲染配合预加载以及 v-cloak ,达到更流畅的加载体验。

合理规划并应用以上方案,可以显著提升 Vuetify 页面加载速度和用户体验。

希望这些解决方案能够帮你应对 Vuetify 样式延迟加载问题,享受流畅的 Web 开发之旅!